The Challenge

Finance has the responsibility to forecast, plan and allocate resources in order to successfully deliver the best treatments to our patients.

  • The first challenge consist of deliver sales forecast for 2018 per cluster, per brand and per month.

  • The second challenge is to provide the optimal allocation of their resources for the year 2018.

Sales Forecasting

The first problem deals with generating the forecasts of 75 time series. Each time series corresponds to the demand for a specific product (brand) in a geographic area (cluster). The time series are in monthly granularity.

We have been provided with the demand history for each of the series, so that by training statistical models in the past, we should be able to model behaviors that are likely to be repeated in the future. Therefore, with time series models we should be able to tackle the problem.

There are different techniques to tackle this problem, the “bottom-up” and the “top-down” approach.

A simple method for generating coherent forecasts is the “bottom-up” approach. This approach involves first generating forecasts for each series at the bottom level, and then summing these to produce forecasts for all the higher level series. Other approaches are the “top-down” whose involve first generating forecasts for the total series, and then disaggregating these down to all the levels.

The method chosen for this problem is the “bottom-up” approach because with this method we do not loss any information due to aggregations and because with only 75 series is a feasible approach.

Recall how the data looks like

id cluster brand date sales_1 sales_2 investment_1 investment_2 investment_3 investment_4 investment_5 investment_6
Cluster 9__Others Cluster 9 Others 2018 Oct 0 0 22.2 0.0 0.0 -1.8 0 0.0
Cluster 9__Others Cluster 9 Others 2018 Nov 0 0 -5.5 -7.0 -1.6 -0.6 0 -2.4
Cluster 9__Others Cluster 9 Others 2018 Dec 0 0 -7.2 -3.1 -1.6 -0.6 0 -2.4


The training process will be divided into the following steps:

  1. Define a list of models to use.
  2. Train the models using the training data
  3. Create the forecasts for the validation data
  4. Evaluate the performance of the models

Evaluation metric

The competition metric is the Mean Absolute Percentage Error (MAPE).

\[ MAPE = \frac{100}{h} \sum^{h}_{t=1}\frac{y_{t} - \hat{y}_{t} }{y_{t}} \]

where \(y_t\) is the actual value and \(\hat{y}_t\) is the predicted value.

Percentage errors have the advantage of being unit-free, and so are frequently used to compare forecast performances between data sets.

Measures based on percentage errors have the disadvantage of being infinite or undefined if \(y_t=0\) for any \(t\) in the period of interest, and having extreme values if any \(y_t\) is close to zero. Makridakis (1993) said that “equal errors above the actual value result in a greater APE than those below the actual value”. He provided an example where \(y_t = 150\) and \(\hat{y}_t = 100\), so that the relative error is \(\frac{50}{150} = 0.33\), in contrast to the situation where \(y_t = 100\) and \(\hat{y}_t = 150\), when relative error would be \(\frac{50}{100} = 0.5\). Thus, MAPE puts a heavier penalty on negative errors (when \(y_t\) < \(\hat{y}_t\)) than on positive errors.

In the x-axis we have the absolute error and in the y-axis de MAPE. Absolute error are the number of units above or below the real value, i.e if the real value is 100 and the error is 50 then the predicted value has been 150. For the same error, as lower the true vale it is, the bigger the error we have.

Split data

As we are dealing with monthly time series data we will split the series using time rather than randomly as we usually do when training machine learning models. We will keep apart the last year of the data.

The datasets will be as follows:

[1] "Train data period: 2012 Jan - 2016 Dec"
[1] "Valid data period: 2017 Jan - 2017 Dec"
[1] "Test data period: 2018 Jan - 2018 Dec"

Baseline models

Before doing nothing, let’s compute some statistical baselines for all the series, that is, statistical models that just repeat the last value (NAIVE) or the last period of samples (SNAIVE).

The models that we are going to use as a benchmarks are:

  • NAIVE: this model replicate the last value of the sales data. This model is equivalent to an \(ARIMA(0,1,0)\) model.

  • SNAIVE: this model replicates the last period of samples from train data. This model is equivalent to an \(ARIMA(0,0,0)(0,1,0)_m\) model, where m is the seasonal period (m = 12 in monthly data.)

# Baseline models
models <- list(
  naive    = NAIVE(sales_2),
  snaive_1 = SNAIVE(sales_2 ~ lag("year") ),
  snaive_2 = SNAIVE(sales_2 ~ lag("year") + drift() )
)

Fit baseline models

fit_baselines <- train_data %>% 
  model(!!!models)

Generate forecast for all the models. Three different forecast will be generated for each serie.

fc_baselines <- fit_baselines %>% 
  forecast(new_data = valid_data)

The metric that we will use to evaluate the performance of each model is the MAPE but I will also include the RMSSE and CRPS for completeness. For both, the RMSE and the MAPE, the smaller the better.

The best model has been the naive with a MAPE of 26.9, followed by the two variants of the snaive. An error of a MAPE of 26.9 means that the model has an error of 26.9%.

.model RMSSE MAPE CRPS
naive 17.1 26.9 1224.8
snaive_1 17.3 29.9 1174.4
snaive_2 17.3 30.1 1197.3

Now that we have the forecast and we know what has been the best model, let’s visualize the forecast to get insight into the data.

Although the snaive model has performed better overall, in some series it performs worse than the snaive.

We also find differences between the snaive models. In the following plot we can see how the snaive with drift is under-forecasting while the other, without drift, is providing an acceptable forecast.

Sometimes no model is good.

Statistical Benchmarks

Exponential smoothing and ARIMA models are the two most widely used approaches to time series forecasting, and provide complementary approaches to the problem. While exponential smoothing models are based on a description of the trend and seasonality in the data, ARIMA models aim to describe the autocorrelations in the data.

  • ARIMA: autoregressive integrated moving average model
  • ETS: exponential smoothing models are based on a description of the trend and seasonality in the data

When there are long seasonal periods, a dynamic regression with fourier terms is often better than ARIMA and ETS models.

For example, daily data can have annual seasonality of length 365, weekly data has seasonal period of approximately 52, while half-hourly data can have several seasonal periods, the shortest of which is the daily pattern of period 48.

Despite we do not have long seasonal periods here, this models will be included in our list of models.

  • FOURIER: Linear regression model with errors modeled with an arima model to remove the autocorrelation.

Additionally to this models, it has also been included a combination forecast of ARIMA and ETS models which has been called combn.

models_stat <- list(
  # Statistical benchmarks
  arima = ARIMA(sales_2),
  ets   = ETS(sales_2),
  
  # Using Fourier terms and ARIMA errors for forecasting
  `K = 1` = ARIMA(sales_2 ~ fourier(K = 1) + PDQ(0, 0, 0)),
  `K = 2` = ARIMA(sales_2 ~ fourier(K = 2) + PDQ(0, 0, 0)),
  `K = 3` = ARIMA(sales_2 ~ fourier(K = 3) + PDQ(0, 0, 0)),
  `K = 4` = ARIMA(sales_2 ~ fourier(K = 4) + PDQ(0, 0, 0)),
  `K = 5` = ARIMA(sales_2 ~ fourier(K = 5) + PDQ(0, 0, 0)),
  `K = 6` = ARIMA(sales_2 ~ fourier(K = 6) + PDQ(0, 0, 0))
)

The best model is the combn, followed by the ARIMA and the K = 2 models. The error has been reduced by 26.4% with respect to the naive model.

.model RMSSE MAPE CRPS
combn 16.7 19.8 797.7
arima 16.9 20.4 827.0
K = 2 16.8 21.6 875.1
ets 16.8 21.8 841.9
K = 5 16.9 21.8 926.6
K = 4 17.0 21.8 935.6

In the example below, neither model has generated a good forecast, in fact, it seems that the ets model only has captured the trend.

In this other case, both forecasts are similar to each other and the snaive_1

Again, none of the models has been able to capture the big drop off of 2017.

External Information

The time series models used so far allow for the inclusion of information from past observations of a series, but not for the inclusion of other information that may also be relevant. For example, the effects of the investments may explain some of the historical variation and may lead to more accurate forecasts

# Using investments as predictors
models_xreg <- list(
  xreg_1 = ARIMA(sales_2 ~ investment_1),
  xreg_2 = ARIMA(sales_2 ~ investment_1 + investment_2),
  xreg_3 = ARIMA(sales_2 ~ investment_1 + investment_2 + investment_3),
  xreg_4 = ARIMA(sales_2 ~ investment_1 + investment_2 + investment_3 + 
                   investment_4 + investment_5 + investment_6)
)

Overall accuracy is not as good as combn, arima orets models trained on statistical benchmarks section.

.model RMSSE MAPE CRPS
xreg_3 17.0 23.9 1065.3
xreg_1 17.1 24.1 958.7
xreg_2 17.1 24.9 1047.7
xreg_4 17.2 25.7 1119.4

Model K = 4 overfit the data and generate a forecast in the opposite direction. In contrast, the K = 2 is able to capture a more general pattern. This is a good example that more complex models do not always indicate better results.

All the forecasts are quite similar in this case

Apparently, the investments neither explain the drop off of 2017.

For cases like this, we are going to introduce a new set of models that will help us to improve this structural changes.

Piecewise regression

We have seen that, in some cases, the trend is not linear and the models are not able to model the series correctly. Piece-wise models divide the series into several segments in such a way that they are able to detect the trend correctly.

Brand analysis

Performing the analysis for each series is a complicated and time-consuming task. For this reason, many times higher levels of aggregation are analyzed and then these conditions or parameters are applied to lower levels series.

The plot of the Brand Group 31 reveals two different periods. There is a clear uptrend up to the end of 2015. After 2015 there is a decrease in the growing speed and the trend slow down a bit. To account for these changes, we specify the date 2015-01 as knots.

# Select a brand a fit a model
current_brand <- "Brand Group 31"

# Fit simple `lm` and `piecewise` model
fit_trends <- by_brand_tsibble %>% 
  filter(brand == current_brand) %>% 
  model(
    lm = TSLM(sales_2 ~ trend()),
    piecewise = TSLM(sales_2 ~ trend(
      knots = tsibble::yearmonth("2015-01")))
  )

In the plot below the relationship between the real and adjusted values by the model is shown. The slashed line represents the perfection, in such a way that the further the points are from the line, the worse the model is. In the lower left corner, the values of the piecewise model are much better adjusted to the reality while those of the lm are farther away. In general, there is a bigger dispersion in the lm model than in the piecewise.

The models are defined manually for each brand.

get_models_ <- function(brand) {
  # Define step-wise models
  mdl <- null_model()
  if (brand == "Brand Group 17") {
    mdl  <- TSLM(sales_2 ~ trend(knots = tsibble::yearmonth("2015-03")) + season())
  } else if (brand == "Brand Group 24") {
    ...
  }
}

In brands such as Brand Group 17 or Brand Group 24 the effect is remarkable. It is also in others, such as the Brand Group 31 as we have just seen in the previous plots.

All series analysis

Now that we have identified the knots of each brand is time to apply this models into the product level series.

fit_stepwise <- map_dfr(
  .x = setNames(brands, brands), 
  .f = ~ train_data %>% 
    filter(brand == .x) %>% 
    model(!!!get_models(.x)["stepwise"]), 
  .id = "brand"
)

Overall accuracy is not as good as combn, arima orets models trained on statistical benchmarks section

.model RMSSE MAPE CRPS
stepwise 16.9 24 1077.7

Let’s look at some of the worst forecast. The arima and ets models will be included as a reference. In that way, we can understand how the piecewise model is modeling the data. In the following case, it seems that the model has failed to capture the trend correctly and is under-forecasting much more than the other two models.

What we can do now? Let’s plot the trend to understand why this is happening. The model fits very well the trend up to 2016 but, after that period, the trend is still in a downtrend and it should remain almost flat. The vertical dashed lines indicate the knots defined within the model.

Finally, we show the forecast for the series that we have been looking in the previous sections.

fit_stepwise %>% 
  filter(id == "Cluster 2__Others") %>%
  left_join(select(fit_statistical, id, ets, arima), by = "id") %>% 
  forecast(new_data = valid_data) %>% 
  plot_forecast(facets = ".model", ncol = NULL, level = NULL, title = "") +
  facet_wrap(~ id)

Selecting best model

Throughout the notebook we have seen how each model is better suited to one specific case, in some cases a model as simple as the naive give us the best results, however, in other cases we need more complex models to capture the signal of the series. So, why not select the best model for each series instead of always using the same model for everything?

# Combine all accuracies together
acc_all <- 
  bind_rows(acc_baselines, acc_statistical, acc_xreg, acc_stepwise) %>% 
  add_rank(var = "MAPE")

# Select best model for each serie
acc_best_models <- acc_all %>% 
  arrange(id, rank) %>% 
  top_n(-rank, n = 1) %>% 
  dplyr::group_by(id) %>% 
  dplyr::slice(1) %>% 
  ungroup()

The error has been reduced by an 47% with respect to the naive and 28% with respect to the combn model.

acc_best_models %>% 
  summarise_at(vars(RMSSE:CRPS), mean) %>% 
  add_column(.model = "Best model", .before = 1) %>% 
  arrange(MAPE) %>% 
  to_html(digits = 1)
.model RMSSE MAPE CRPS
Best model 16.5 14.1 602.1

How many times each model has been selected? stepwise and arima are the two best models followed by xreg_4. Recall that this model uses all the investments provided.

In the same way that we have been doing, we are going to show the best and worst predictions to get an idea of what the predictions are like.

# Combine all forecasts
fc_all_models <- bind_rows(fc_baselines, fc_statistical, fc_xreg, fc_stepwise)

fc_best_models <- fc_all_models %>% 
  semi_join(acc_best_models, by = c(".model", "id"))

Sometimes, any model is able to predict the data accurately. Might be, because the series are white noise or the signal-to-noise ratio is very low. Other times, there are very abrupt changes that cannot be explained by any predictor (past sales, investments, etc.)

Drill down accuracy

Forecasts are often required for all levels of the series, and it is natural that the forecasts to add up in the same way as the data. This is important because the forecast will be analyzed from different points of view and all of them must be aligned. For example, a brand manager might be interested in the forecast of all of their products but it also would might get a view at country level of their brand. At the same time, the CEO of the company, might be interested in higher levels of aggregation.

The easiest brand to forecast is Brand Group 96, 97 and the hardest is Brand Group 24 which is not a surprise given it’s highest growth.

.model brand RMSSE MAPE CRPS
Best model Brand Group 96, 97 0.5 6.6 208.9
Best model Others 0.4 6.8 1315.0
Best model Brand Group 31 0.3 6.9 765.8
Best model Brand Group 41 0.5 7.7 470.8
Best model Brand Group 51, 73, 90 0.4 8.2 281.9
Best model Brand Group 17 0.5 9.0 362.4
Best model Brand Group 30 0.5 9.1 444.1
Best model Brand Group 36 1.2 19.2 178.2
Best model Brand Group 24 1.4 20.1 227.3

In the same way, the easiest cluster is Cluster 8 and the hardest is Cluster 7

.model cluster RMSSE MAPE CRPS
Best model Cluster 8 0.6 6.2 270.8
Best model Cluster 2 0.5 6.3 512.4
Best model Cluster 10 0.5 6.6 479.5
Best model Cluster 9 0.5 8.1 168.1
Best model Cluster 1 0.3 8.9 386.3
Best model Cluster 3 0.5 9.7 1018.2
Best model Cluster 4 0.6 16.0 644.1
Best model Cluster 5 0.9 16.4 208.9
Best model Cluster 7 0.9 27.4 275.4

Make forecasts

The last step is generate the forecast for all the 75 series. Now, we must estimate the models again but using all the data available.

# Combine all three list of models
models_list <- c(models, models_stat, models_xreg)

# Fit models using all train data
fit_all_models_1 <- train_data %>%
  bind_rows(valid_data) %>%
  model(!!!models_list) %>%
  mutate(combn = (arima + ets) / 2)

# Train piece-wise models
fit_all_models_2 <- map_dfr(
  .x = setNames(brands, brands),
  .f = ~ train_data %>%
    bind_rows(valid_data) %>%
    filter(brand == .x) %>%
    model(!!!get_models(.x)["stepwise"]),
  .id = "brand"
)

# Combine all models together
fit_all_models <- fit_all_models_1 %>% 
  left_join(fit_all_models_2, by = "id")

# Save models
write_rds(fit_all_models, 
  file.path(path_models, "fit_all_models.rds"), 
  compress = "gz")

Resource Allocation

Resource allocation methods can be used in almost every aspect of supply chain management. The common goal is allocate the resources effectively (i.e., maximizing the revenue, minimizing the cost, or optimizing the utilization sequence) while satisfying certain constraints (e.g., resource availability, customer service level, back order level, delivery window). In this case, resources refers to investments.

In this second part of the challenge, Novartis ask to the participants to provide the optimal allocation of their resources for the year 2018, as well as, showing differences of allocations by cluster/brand/investment type and impact on sales. Investments are now compressed into Investment 1, Investment 2 and Others.

The template file provided by the organization looks as follow:

cluster brand function OptimiseInvestment ForecastedSales OptimiseSales
Cluster 1 Brand Group 17 Investment 1 NA NA NA
Cluster 1 Brand Group 17 Investment 2 NA NA NA
Cluster 1 Brand Group 17 others NA NA NA

Create the new variable investment_others, and remove unnecessary columns.

alloc_df <- all_levels_tbl %>% 
  mutate(
    investment_others = investment_3 + investment_4 + 
      investment_5 + investment_6
  ) %>% 
  select(-c(investment_3, investment_4, investment_5, investment_6))
id cluster brand date sales_1 sales_2 investment_1 investment_2 investment_others
Cluster 1__Brand Group 17 Cluster 1 Brand Group 17 2012 Jan 0 0 0 0 0
Cluster 1__Brand Group 17 Cluster 1 Brand Group 17 2012 Feb 0 0 0 0 0
Cluster 1__Brand Group 17 Cluster 1 Brand Group 17 2012 Mar 0 0 0 0 0

Forecasting model

We need a forecasting model with investments as predictors. Ideally, the model obtained in the first challenge is the one that we should use to perform the optimization. However, some of the models are univariate and in any case the variable investment_others has been used. Therefore, a linear regression model will be used, which will use the trend, the seasonality and the investments as predictors.

Note: we should look for the best model possible that contains investments as predictors.

models_alloc <- list(
  lm = TSLM(sales_2 ~ trend() + season() + investment_1 + investment_2 +
              investment_others)
)

Then, train the models and generate the forecasts for the next 12 months

# Train models
fit_alloc <- train_data_alloc %>% 
  model(!!!models_alloc)
# Generate forecasts for the test period
fc_alloc <- fit_alloc %>% 
  forecast(new_data = test_data_alloc)

Simulations

When a closed form is not available to estimate the value of a parameter, either because it does not have a closed form formula or because there are too many factor that influence in the final result, the use of simulation techniques are commonly used to observe the behavior of the variable to be analyzed.

The model will be fed with variations of the investments to obtain different forecasts. Sometimes reducing the amount invested on one side to place it in another can lead to better results, and vice versa.

The steps to follow are:

  1. Create a matrix with the weights combinations
  2. Calculate the new simulated investments
  3. Generate the forecast with the new simulated values

Simulation weights

We create a mesh of values that allow us to evaluate different scenarios. The following restrictions have been applied:

  • The weights must add up to 1

  • In order to diversify, the maximum allocation will be 80% of the capital.

comb_id comb_desc investment_1 investment_2 investment_others
1 [0.00, 0.20, 0.80] 0 0.20 0.80
2 [0.00, 0.25, 0.75] 0 0.25 0.75
3 [0.00, 0.30, 0.70] 0 0.30 0.70
4 [0.00, 0.35, 0.65] 0 0.35 0.65
5 [0.00, 0.40, 0.60] 0 0.40 0.60
6 [0.00, 0.45, 0.55] 0 0.45 0.55

A total of 189 combinations per series will be evaluated.

Create scenarios

To generate the multiple scenarios we must multiply the weights generated in the previous section with the values of the investments. To do this, we will help ourselves with the following function:

get_future_scenerarios <- function(future_data, weights_df) {
  
  # Store id data
  id_df <- future_data %>% 
    select(id, date)
  
  # Calculate total invested in each product-month
  total_invested <- future_data %>% 
    transmute(total = rowSums(across(contains("investment"))))
  
  # Repeat `total_invested` column 3 times (one for each investment)
  inv <- replicate(n = 3, total_invested$total) 
  
  # Loop through weights
  inv_cols <- str_subset(names(weights_df), "investment")
  scenarios_list <- vector("list", length = nrow(weights_df))
  for (i in seq(1, nrow(weights_df))) {
    
    # Weights for this iteration
    w <- weights_df[i, inv_cols]
    w <- as.numeric(w)
    
    # New investments
    new_investments <- t(w * t(inv))
    colnames(new_investments) <- inv_cols

    future_scenario <- id_df %>% 
      bind_cols(weights_df[i, "comb_id"]) %>% 
      bind_cols(as_tibble(new_investments))
    
    scenarios_list[[i]] <- future_scenario
  }
  
  # Return a list with all the simulations
  scenarios_list
}

In gray-blue color we see the possible scenarios of the forecast. Each scenario corresponds to a different weight combination. In orange we have the median and in another shade of blue the 80th quantile.

Find optimal values

We will use the 80th quantile as the best combination. We could use the combination of parameters that has generated the greatest benefits, but it would be select the best optimistic scenario and we prefer to be more conservative.

First, we calculate the total amount of each simulation

simulations_agg <- fc %>% 
  as_tibble() %>% 
  group_by(id, comb_id) %>% 
  summarise(across(contains(".mean") | contains("investment"), sum)) %>%
  ungroup() %>% 
  rename(OptimalSales = .mean) %>% 
  arrange(desc(OptimalSales)) %>% 
  left_join(select(weights_df, comb_id, comb_desc), by = "comb_id")

Then, select the combination that meets our requirements: pct_rank <= 0.8

# Find best combination of weights for each `id`
optimal_simulation_id <- simulations_agg %>% 
  group_by(id) %>%
  summarise(
    comb_id = comb_id,
    OptimalSales = OptimalSales,
    pct_rank = percent_rank(OptimalSales)
  ) %>% 
  # Select 80 percentile
  filter(pct_rank <= 0.8) %>% 
  slice_max(pct_rank) %>%
  # In case of tie, choose the first one. This happens
  # in `Cluster 5__Brand Group 30`
  slice_head(n = 1) %>% 
  ungroup()

Finally, prepare the output data

id function OptimiseInvestment ForecastedSales OptimalSales
Cluster 3__Brand Group 31 investment_1 -5851 427441 442260
Cluster 3__Brand Group 31 investment_2 -4388 427441 442260
Cluster 3__Brand Group 31 investment_others -19016 427441 442260
Cluster 2__Brand Group 41 investment_1 -1311 354095 412700
Cluster 2__Brand Group 41 investment_2 -13107 354095 412700
Cluster 2__Brand Group 41 investment_others -11796 354095 412700

According to the simulation results, we could have had a profit of ~ 500k in 2018.

Year OptimiseInvestment ForecastedSales OptimalSales Profit
2018 -457078 7366401 7885967 519566

Evolution of investments

The evolution of the investments over the years is shown below.It can be seen how, in general terms, the investments for 2018 are aligned with the historical trends and do not different sharply from the recent past. However, there are some investments that have been affected more than the rest.

In general, it is suggested to invest more in others for the 2018.

What’s next

I will leave some ideas that be explored to expand the analysis

  • Remove outliers and clean the data.
  • Time series clustering based on features such as trend strength, seasonality, spikiness, linearity, autocorrelation, etc.
  • Test if ensembles do improve the accuracy. There are many types of ensembles to explore: combination ensembles, weighted average ensembles based on cross validation errors or even more advanced using regularized linear models to name a few.
  • Use more than one fold to select the best models, i.e use time series cross validation.
  • Directly find the best model in the first challenge so it can be used in the optimization problem.
  • Try others methods such as prophet, machine learning or others.
  • Account for uncertainty measures, because all is not about accuracy, prediction intervals are also important.
  • Try other optimization methods
LS0tDQp0aXRsZTogIlRpbWUgU2VyaWVzIE1vZGVsaW5nIg0KYXV0aG9yOiAiQ2FybG9zIEVzcGVsZXRhIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDINCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IG5vDQogICAgICBzbW9vdGhfc2Nyb2xsOiBubw0KICAgIGNvZGVfZm9sZGluZzogbm9uZQ0KICAgIHRoZW1lOiBjb3Ntbw0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICBjc3M6IGluY2x1ZGUvY2VudGVyLmNzcw0KICAgIGluY2x1ZGVzOg0KICAgICAgaW5faGVhZGVyOiBpbmNsdWRlL2Zhdmljb24uaHRtbA0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogOTANCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGVjaG8gPSBGQUxTRSwgDQogIGZpZy53aWR0aCA9IDcsIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCANCiAgb3V0LndpZHRoID0gIjk1JSIsIGRwaSA9IDcyDQopDQpgYGANCg0KYGBge3IgbG9hZC1saWJyYXJpZXMsIGVjaG89RkFMU0V9DQojIExvYWQgcGFja2FnZXMNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkocGF0Y2h3b3JrKQ0KbGlicmFyeShmdXR1cmUpDQpsaWJyYXJ5KHRzaWJibGUpDQpsaWJyYXJ5KGZhYmxlKQ0KbGlicmFyeShnZ3RoZW1lcykNCg0KIyBBdm9pZCBtZXNzYWdlIG9uIHN1bW1hcml6ZQ0Kb3B0aW9ucyhkcGx5ci5zdW1tYXJpc2UuaW5mb3JtID0gRkFMU0UpDQoNCiMgU2V0IGRlZmF1bHQgdGhlbWUgc2V0dGluZ3MNCnRoZW1lX3NldCgNCiAgdGhlbWVfbGlnaHQoKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQopDQoNCiMgRW5nbGlzaA0KaW52aXNpYmxlKFN5cy5zZXRsb2NhbGUoIkxDX0FMTCIsICJFbmdsaXNoIikpDQoNCiMgRGVmaW5lIHBhdGhzIGRpcmVjdG9yaWVzDQpwYXRoX2RhdGEgPC0gIi4uL0RhdGEiDQpwYXRoX21vZGVscyA8LSAiLi4vTW9kZWxzIg0KDQojIERlZmluZSBsYXN0IHRyYWluIGRhdGUNCmxhc3RfdHJhaW4gPC0gIjIwMTgtMDEtMDEiDQoNCiMgU2V0IFRSVUUgdG8gdHJhaW4gbW9kZWxzLCBvdGhlcndpc2Ugc2V0IHRvIEZBTFNFLg0KaXNfdHJhaW5fbW9kZWxzIDwtIEZBTFNFDQpgYGANCg0KYGBge3Iga25pdHItdGFibGUtYXV4fQ0KdG9faHRtbCA8LSBmdW5jdGlvbihkZiwgZGlnaXRzID0gMiwgLi4uKSB7DQogIGtuaXRyOjprYWJsZShkZiwgZm9ybWF0ID0gImh0bWwiLCBkaWdpdHMpICU+JSANCiAgICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKA0KICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLA0KICAgICAgLi4uDQogICAgKQ0KfQ0KYGBgDQoNCmBgYHtyIGxvZ28tbm92YXJ0aXMsIGVjaG89RkFMU0UsIG91dC53aWR0aD0nMTAwJSd9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnLi4vSW1hZ2VzL2xvZ28tbm92YXJ0aXMucG5nJykNCmBgYA0KDQojIFRoZSBDaGFsbGVuZ2Ugey51bm51bWJlcmVkfQ0KDQpGaW5hbmNlIGhhcyB0aGUgcmVzcG9uc2liaWxpdHkgdG8gZm9yZWNhc3QsIHBsYW4gYW5kIGFsbG9jYXRlIHJlc291cmNlcyBpbiBvcmRlciB0bw0Kc3VjY2Vzc2Z1bGx5IGRlbGl2ZXIgdGhlIGJlc3QgdHJlYXRtZW50cyB0byBvdXIgcGF0aWVudHMuDQoNCi0gICBUaGUgKipmaXJzdCoqIGNoYWxsZW5nZSBjb25zaXN0IG9mIGRlbGl2ZXIgc2FsZXMgZm9yZWNhc3QgZm9yIDIwMTggcGVyIGNsdXN0ZXIsIHBlcg0KICAgIGJyYW5kIGFuZCBwZXIgbW9udGguDQoNCi0gICBUaGUgKipzZWNvbmQqKiBjaGFsbGVuZ2UgaXMgdG8gcHJvdmlkZSB0aGUgb3B0aW1hbCBhbGxvY2F0aW9uIG9mIHRoZWlyIHJlc291cmNlcyBmb3INCiAgICB0aGUgeWVhciAyMDE4Lg0KDQojIFNhbGVzIEZvcmVjYXN0aW5nDQoNClRoZSBmaXJzdCBwcm9ibGVtIGRlYWxzIHdpdGggZ2VuZXJhdGluZyB0aGUgZm9yZWNhc3RzIG9mIDc1IHRpbWUgc2VyaWVzLiBFYWNoIHRpbWUgc2VyaWVzDQpjb3JyZXNwb25kcyB0byB0aGUgZGVtYW5kIGZvciBhIHNwZWNpZmljIHByb2R1Y3QgKGJyYW5kKSBpbiBhIGdlb2dyYXBoaWMgYXJlYSAoY2x1c3RlcikuDQpUaGUgdGltZSBzZXJpZXMgYXJlIGluIG1vbnRobHkgZ3JhbnVsYXJpdHkuDQoNCldlIGhhdmUgYmVlbiBwcm92aWRlZCB3aXRoIHRoZSBkZW1hbmQgaGlzdG9yeSBmb3IgZWFjaCBvZiB0aGUgc2VyaWVzLCBzbyB0aGF0IGJ5IHRyYWluaW5nDQpzdGF0aXN0aWNhbCBtb2RlbHMgaW4gdGhlIHBhc3QsIHdlIHNob3VsZCBiZSBhYmxlIHRvIG1vZGVsIGJlaGF2aW9ycyB0aGF0IGFyZSBsaWtlbHkgdG8gYmUNCnJlcGVhdGVkIGluIHRoZSBmdXR1cmUuIFRoZXJlZm9yZSwgd2l0aCB0aW1lIHNlcmllcyBtb2RlbHMgd2Ugc2hvdWxkIGJlIGFibGUgdG8gdGFja2xlIHRoZQ0KcHJvYmxlbS4NCg0KVGhlcmUgYXJlIGRpZmZlcmVudCB0ZWNobmlxdWVzIHRvIHRhY2tsZSB0aGlzIHByb2JsZW0sIHRoZSAiYm90dG9tLXVwIiBhbmQgdGhlICJ0b3AtZG93biINCmFwcHJvYWNoLg0KDQpBIHNpbXBsZSBtZXRob2QgZm9yIGdlbmVyYXRpbmcgY29oZXJlbnQgZm9yZWNhc3RzIGlzIHRoZSAiYm90dG9tLXVwIiBhcHByb2FjaC4gVGhpcw0KYXBwcm9hY2ggaW52b2x2ZXMgZmlyc3QgZ2VuZXJhdGluZyBmb3JlY2FzdHMgZm9yIGVhY2ggc2VyaWVzIGF0IHRoZSBib3R0b20gbGV2ZWwsIGFuZCB0aGVuDQpzdW1taW5nIHRoZXNlIHRvIHByb2R1Y2UgZm9yZWNhc3RzIGZvciBhbGwgdGhlIGhpZ2hlciBsZXZlbCBzZXJpZXMuIE90aGVyIGFwcHJvYWNoZXMgYXJlDQp0aGUgInRvcC1kb3duIiB3aG9zZSBpbnZvbHZlIGZpcnN0IGdlbmVyYXRpbmcgZm9yZWNhc3RzIGZvciB0aGUgdG90YWwgc2VyaWVzLCBhbmQgdGhlbg0KZGlzYWdncmVnYXRpbmcgdGhlc2UgZG93biB0byBhbGwgdGhlIGxldmVscy4NCg0KVGhlIG1ldGhvZCBjaG9zZW4gZm9yIHRoaXMgcHJvYmxlbSBpcyB0aGUgImJvdHRvbS11cCIgYXBwcm9hY2ggYmVjYXVzZSB3aXRoIHRoaXMgbWV0aG9kIHdlDQpkbyBub3QgbG9zcyBhbnkgaW5mb3JtYXRpb24gZHVlIHRvIGFnZ3JlZ2F0aW9ucyBhbmQgYmVjYXVzZSB3aXRoIG9ubHkgNzUgc2VyaWVzIGlzIGENCmZlYXNpYmxlIGFwcHJvYWNoLg0KDQpgYGB7ciBsb2FkLWRhdGF9DQphbGxfbGV2ZWxzIDwtIHJlYWRfcmRzKGZpbGUucGF0aChwYXRoX2RhdGEsICJkYXRhX2xldmVsX3NrdS5yZHMiKSkNCmJ5X2JyYW5kIDwtIHJlYWRfcmRzKGZpbGUucGF0aChwYXRoX2RhdGEsICJkYXRhX2xldmVsX2JyYW5kLnJkcyIpKQ0KYGBgDQoNClJlY2FsbCBob3cgdGhlIGRhdGEgbG9va3MgbGlrZQ0KDQpgYGB7ciBjb252ZXJ0LXRvLXRzaWJibGV9DQojIERlZmluZSBga2V5c2AgYW5kIGBpbmRleGANCmtleSA8LSBjKCJjbHVzdGVyIiwgImJyYW5kIikNCmluZGV4IDwtICJkYXRlIg0KDQojIENvbnZlcnQgdG8gYSBgdHNpYmJsZWAgb2JqZWN0DQphbGxfbGV2ZWxzX3RibCA8LSBhbGxfbGV2ZWxzICU+JSANCiAgdW5pdGUoaWQsICEha2V5LCBzZXAgPSAiX18iLCByZW1vdmUgPSBGQUxTRSkgJT4lIA0KICBtdXRhdGUoZGF0ZSA9IHRzaWJibGU6OnllYXJtb250aCghIXN5bShpbmRleCkpKSAlPiUNCiAgYXNfdHNpYmJsZShrZXkgPSBpZCwgaW5kZXggPSBpbmRleCkNCg0KIyBQcmV2aWV3IGRhdGENCnRhaWwoYWxsX2xldmVsc190YmwsIDMpICU+JSANCiAgdG9faHRtbChkaWdpdHMgPSAxKSAlPiUgDQogIGthYmxlRXh0cmE6OnNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KPGJyPg0KDQpUaGUgdHJhaW5pbmcgcHJvY2VzcyB3aWxsIGJlIGRpdmlkZWQgaW50byB0aGUgZm9sbG93aW5nIHN0ZXBzOg0KDQoxLiAgRGVmaW5lIGEgbGlzdCBvZiBtb2RlbHMgdG8gdXNlLg0KMi4gIFRyYWluIHRoZSBtb2RlbHMgdXNpbmcgdGhlIHRyYWluaW5nIGRhdGENCjMuICBDcmVhdGUgdGhlIGZvcmVjYXN0cyBmb3IgdGhlIHZhbGlkYXRpb24gZGF0YQ0KNC4gIEV2YWx1YXRlIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgbW9kZWxzDQoNCiMjIEV2YWx1YXRpb24gbWV0cmljDQoNClRoZSBjb21wZXRpdGlvbiBtZXRyaWMgaXMgdGhlIE1lYW4gQWJzb2x1dGUgUGVyY2VudGFnZSBFcnJvciAoTUFQRSkuDQoNCiQkDQpNQVBFID0gXGZyYWN7MTAwfXtofSBcc3VtXntofV97dD0xfVxmcmFje3lfe3R9IC0gXGhhdHt5fV97dH0gfXt5X3t0fX0NCiQkDQoNCndoZXJlICR5X3QkIGlzIHRoZSBhY3R1YWwgdmFsdWUgYW5kICRcaGF0e3l9X3QkIGlzIHRoZSBwcmVkaWN0ZWQgdmFsdWUuDQoNClBlcmNlbnRhZ2UgZXJyb3JzIGhhdmUgdGhlIGFkdmFudGFnZSBvZiBiZWluZyB1bml0LWZyZWUsIGFuZCBzbyBhcmUgZnJlcXVlbnRseSB1c2VkIHRvDQpjb21wYXJlIGZvcmVjYXN0IHBlcmZvcm1hbmNlcyBiZXR3ZWVuIGRhdGEgc2V0cy4NCg0KTWVhc3VyZXMgYmFzZWQgb24gcGVyY2VudGFnZSBlcnJvcnMgaGF2ZSB0aGUgZGlzYWR2YW50YWdlIG9mIGJlaW5nIGluZmluaXRlIG9yIHVuZGVmaW5lZA0KaWYgJHlfdD0wJCBmb3IgYW55ICR0JCBpbiB0aGUgcGVyaW9kIG9mIGludGVyZXN0LCBhbmQgaGF2aW5nIGV4dHJlbWUgdmFsdWVzIGlmIGFueSAkeV90JA0KaXMgY2xvc2UgdG8gemVyby4gW01ha3JpZGFraXMgKDE5OTMpXShodHRwOi8vZHguZG9pLm9yZy8xMC4xMDE2LzAxNjktMjA3MCUyODkzJTI5OTAwNzktMykNCnNhaWQgdGhhdCAiZXF1YWwgZXJyb3JzIGFib3ZlIHRoZSBhY3R1YWwgdmFsdWUgcmVzdWx0IGluIGEgZ3JlYXRlciBBUEUgdGhhbiB0aG9zZSBiZWxvdw0KdGhlIGFjdHVhbCB2YWx1ZSIuIEhlIHByb3ZpZGVkIGFuIGV4YW1wbGUgd2hlcmUgJHlfdCA9IDE1MCQgYW5kICRcaGF0e3l9X3QgPSAxMDAkLCBzbyB0aGF0DQp0aGUgcmVsYXRpdmUgZXJyb3IgaXMgJFxmcmFjezUwfXsxNTB9ID0gMC4zMyQsIGluIGNvbnRyYXN0IHRvIHRoZSBzaXR1YXRpb24gd2hlcmUNCiR5X3QgPSAxMDAkIGFuZCAkXGhhdHt5fV90ID0gMTUwJCwgd2hlbiByZWxhdGl2ZSBlcnJvciB3b3VsZCBiZSAkXGZyYWN7NTB9ezEwMH0gPSAwLjUkLg0KVGh1cywgTUFQRSBwdXRzIGEgaGVhdmllciBwZW5hbHR5IG9uIG5lZ2F0aXZlIGVycm9ycyAod2hlbiAkeV90JCBcPCAkXGhhdHt5fV90JCkgdGhhbiBvbg0KcG9zaXRpdmUgZXJyb3JzLg0KDQpgYGB7ciBkZWZpbmUtZXZhbC1tZXRyaWN9DQptYXBlXyA8LSBmdW5jdGlvbih5X3RydWUsIHlfcHJlZCkgew0KICBtZWFuKGFicyggKHlfdHJ1ZSAtIHlfcHJlZCkgLyB5X3RydWUpKQ0KfQ0KDQpkZl8gPC0gY3Jvc3NpbmcoDQogIHlfdHJ1ZSA9IGMoMTUwLCAxMDAsIDUwKSwgDQogIGVycm9yID0gc2VxKGZyb20gPSAtMTAwLCB0byA9IDEwMCwgbGVuZ3RoLm91dCA9IDEwMCkNCiAgKSAlPiUgDQogIG11dGF0ZSgNCiAgICB5X3ByZWQgPSB5X3RydWUgKyBlcnJvciwNCiAgICBtZXRyaWNfdmFsdWUgPSBtYXAyX2RibCh5X3RydWUsIHlfcHJlZCwgbWFwZV8pDQogICkNCg0KaGlnaGxpZ2h0X2RmIDwtIGRmXyAlPiUgDQogIGdyb3VwX2J5KHlfdHJ1ZSkgJT4lIA0KICBmaWx0ZXIobWV0cmljX3ZhbHVlID09IG1heChtZXRyaWNfdmFsdWUpKSAlPiUgDQogIHNsaWNlX21heCh5X3ByZWQpICU+JSANCiAgdW5ncm91cCgpDQoNCmdncGxvdChkZl8sIGFlcyhlcnJvciwgbWV0cmljX3ZhbHVlKSkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZSA9IDIsIGFscGhhID0gMC42LCBjb2xvciA9ICJncmF5NTAiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDEwMCwgbGluZXR5cGUgPSAyLCBhbHBoYSA9IDAuNiwgY29sb3IgPSAiZ3JheTUwIikgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gZmFjdG9yKHlfdHJ1ZSkpKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGhpZ2hsaWdodF9kZiwgY29sb3IgPSAid2hpdGUiLCANCiAgICAgICAgICAgICBmaWxsID0gInBpbmsiLCBzaXplID0gNC41LCBzaGFwZSA9IDIxLCBhbHBoYSA9IDAuNykgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKG4uYnJlYWtzID0gOSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjo6cGVyY2VudCkgKw0KICAjIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsNCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICBsYWJzKHggPSAiRXJyb3IgaW4gYWJzb2x1dGUgdGVybXMiLCB5ID0gIk1BUEUiLCBjb2xvciA9ICJSZWFsIHZhbHVlIikNCmBgYA0KDQpJbiB0aGUgKngtYXhpcyogd2UgaGF2ZSB0aGUgYWJzb2x1dGUgZXJyb3IgYW5kIGluIHRoZSAqeS1heGlzKiBkZSBNQVBFLiBBYnNvbHV0ZSBlcnJvciBhcmUNCnRoZSBudW1iZXIgb2YgdW5pdHMgYWJvdmUgb3IgYmVsb3cgdGhlIHJlYWwgdmFsdWUsIGkuZSBpZiB0aGUgcmVhbCB2YWx1ZSBpcyAxMDAgYW5kIHRoZQ0KZXJyb3IgaXMgNTAgdGhlbiB0aGUgcHJlZGljdGVkIHZhbHVlIGhhcyBiZWVuIDE1MC4gRm9yIHRoZSBzYW1lIGVycm9yLCBhcyBsb3dlciB0aGUgdHJ1ZQ0KdmFsZSBpdCBpcywgdGhlIGJpZ2dlciB0aGUgZXJyb3Igd2UgaGF2ZS4NCg0KIyMgU3BsaXQgZGF0YQ0KDQpBcyB3ZSBhcmUgZGVhbGluZyB3aXRoIG1vbnRobHkgdGltZSBzZXJpZXMgZGF0YSB3ZSB3aWxsIHNwbGl0IHRoZSBzZXJpZXMgdXNpbmcgdGltZSByYXRoZXINCnRoYW4gcmFuZG9tbHkgYXMgd2UgdXN1YWxseSBkbyB3aGVuIHRyYWluaW5nIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzLiBXZSB3aWxsIGtlZXAgYXBhcnQNCnRoZSBsYXN0IHllYXIgb2YgdGhlIGRhdGEuDQoNCmBgYHtyIHNwbGl0LWRhdGF9DQojIFRyYWluLCB2YWxpZGF0aW9uIGFuZCB0ZXN0IGV2YWx1YXRpb24NCmZpcnN0X2RhdGVfc3VibWlzc2lvbiA8LSB5ZWFybW9udGgoIjIwMTgtMDEtMDEiKQ0KbGFzdF90cmFpbiA8LSBmaXJzdF9kYXRlX3N1Ym1pc3Npb24gLSBtb250aCgxMikNCg0KdHJhaW5fZGF0YSA8LSBhbGxfbGV2ZWxzX3RibCAlPiUgDQogIGZpbHRlcihkYXRlIDwgeWVhcm1vbnRoKGxhc3RfdHJhaW4pKQ0KDQp2YWxpZF9kYXRhIDwtIGFsbF9sZXZlbHNfdGJsICU+JSANCiAgZmlsdGVyKGRhdGUgPj0geWVhcm1vbnRoKGxhc3RfdHJhaW4pLCANCiAgICAgICAgIGRhdGUgPCB5ZWFybW9udGgoZmlyc3RfZGF0ZV9zdWJtaXNzaW9uKSkNCg0KdGVzdF9kYXRhIDwtIGFsbF9sZXZlbHNfdGJsICU+JSANCiAgZmlsdGVyKGRhdGUgPj0geWVhcm1vbnRoKGZpcnN0X2RhdGVfc3VibWlzc2lvbikpDQpgYGANCg0KVGhlIGRhdGFzZXRzIHdpbGwgYmUgYXMgZm9sbG93czoNCg0KYGBge3Igc2hvdy1zcGxpdC1kYXRhLWRhdGUtcmFuZ2VzfQ0Kc3ByaW50ZigiVHJhaW4gZGF0YSBwZXJpb2Q6ICVzIC0gJXMiLCBtaW4odHJhaW5fZGF0YSRkYXRlKSwgbWF4KHRyYWluX2RhdGEkZGF0ZSkpDQpzcHJpbnRmKCJWYWxpZCBkYXRhIHBlcmlvZDogJXMgLSAlcyIsIG1pbih2YWxpZF9kYXRhJGRhdGUpLCBtYXgodmFsaWRfZGF0YSRkYXRlKSkNCnNwcmludGYoIlRlc3QgZGF0YSBwZXJpb2Q6ICVzIC0gJXMiLCBtaW4odGVzdF9kYXRhJGRhdGUpLCBtYXgodGVzdF9kYXRhJGRhdGUpKQ0KYGBgDQoNCiMjIEJhc2VsaW5lIG1vZGVscw0KDQpCZWZvcmUgZG9pbmcgbm90aGluZywgbGV0J3MgY29tcHV0ZSBzb21lIHN0YXRpc3RpY2FsIGJhc2VsaW5lcyBmb3IgYWxsIHRoZSBzZXJpZXMsIHRoYXQNCmlzLCBzdGF0aXN0aWNhbCBtb2RlbHMgdGhhdCBqdXN0IHJlcGVhdCB0aGUgbGFzdCB2YWx1ZSAoYE5BSVZFYCkgb3IgdGhlIGxhc3QgcGVyaW9kIG9mDQpzYW1wbGVzIChgU05BSVZFYCkuDQoNCmBgYHtyIHN0YXJ0LXBhcmFsbGVsaXphdGlvbiwgZWNobz1GQUxTRX0NCiMgIyBOb3Qgc3VwcG9ydGVkIG9uIFdpbmRvd3MuDQojIG15X3BsYW4gPC0gZnV0dXJlOjpwbGFuKCJtdWx0aWNvcmUiLCB3b3JrZXJzID0gNEwpDQojIGZ1dHVyZTo6cmVzZXRXb3JrZXJzKG15X3BsYW4pDQoNCiMgSSB0aGluayB0aGlzIHdvcmtzIG9uIHdpbmRvd3MNCm15X3BsYW4gPC0gZnV0dXJlOjpwbGFuKCJtdWx0aXNlc3Npb24iLCB3b3JrZXJzID0gNEwpDQojIGZ1dHVyZTo6cGxhbigic2VxdWVudGlhbCIpDQpgYGANCg0KVGhlIG1vZGVscyB0aGF0IHdlIGFyZSBnb2luZyB0byB1c2UgYXMgYSBiZW5jaG1hcmtzIGFyZToNCg0KLSAgIGBOQUlWRWA6IHRoaXMgbW9kZWwgcmVwbGljYXRlIHRoZSBsYXN0IHZhbHVlIG9mIHRoZSBzYWxlcyBkYXRhLiBUaGlzIG1vZGVsIGlzDQogICAgZXF1aXZhbGVudCB0byBhbiAkQVJJTUEoMCwxLDApJCBtb2RlbC4NCg0KLSAgIGBTTkFJVkVgOiB0aGlzIG1vZGVsIHJlcGxpY2F0ZXMgdGhlIGxhc3QgcGVyaW9kIG9mIHNhbXBsZXMgZnJvbSB0cmFpbiBkYXRhLiBUaGlzIG1vZGVsDQogICAgaXMgZXF1aXZhbGVudCB0byBhbiAkQVJJTUEoMCwwLDApKDAsMSwwKV9tJCBtb2RlbCwgd2hlcmUgKm0qIGlzIHRoZSBzZWFzb25hbCBwZXJpb2QgKG0NCiAgICA9IDEyIGluIG1vbnRobHkgZGF0YS4pDQoNCmBgYHtyIGJhc2VsaW5lLWRlZmluaXRpb24sIGVjaG89VFJVRX0NCiMgQmFzZWxpbmUgbW9kZWxzDQptb2RlbHMgPC0gbGlzdCgNCiAgbmFpdmUgICAgPSBOQUlWRShzYWxlc18yKSwNCiAgc25haXZlXzEgPSBTTkFJVkUoc2FsZXNfMiB+IGxhZygieWVhciIpICksDQogIHNuYWl2ZV8yID0gU05BSVZFKHNhbGVzXzIgfiBsYWcoInllYXIiKSArIGRyaWZ0KCkgKQ0KKQ0KYGBgDQoNCkZpdCBiYXNlbGluZSBtb2RlbHMNCg0KYGBge3IgYmFzZWxpbmUtZm9yZWNhc3QsIGVjaG89VFJVRX0NCmZpdF9iYXNlbGluZXMgPC0gdHJhaW5fZGF0YSAlPiUgDQogIG1vZGVsKCEhIW1vZGVscykNCmBgYA0KDQpHZW5lcmF0ZSBmb3JlY2FzdCBmb3IgYWxsIHRoZSBtb2RlbHMuIFRocmVlIGRpZmZlcmVudCBmb3JlY2FzdCB3aWxsIGJlIGdlbmVyYXRlZCBmb3IgZWFjaA0Kc2VyaWUuDQoNCmBgYHtyIGJhc2VsaW5lLXZhbGlkYXRpb24tZm9yZWNhc3QsIGVjaG89VFJVRX0NCmZjX2Jhc2VsaW5lcyA8LSBmaXRfYmFzZWxpbmVzICU+JSANCiAgZm9yZWNhc3QobmV3X2RhdGEgPSB2YWxpZF9kYXRhKQ0KYGBgDQoNClRoZSBtZXRyaWMgdGhhdCB3ZSB3aWxsIHVzZSB0byBldmFsdWF0ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgZWFjaCBtb2RlbCBpcyB0aGUgKk1BUEUqIGJ1dCBJDQp3aWxsIGFsc28gaW5jbHVkZSB0aGUgKlJNU1NFKiBhbmQgKkNSUFMqIGZvciBjb21wbGV0ZW5lc3MuIEZvciBib3RoLCB0aGUgKlJNU0UqIGFuZCB0aGUNCipNQVBFLCogdGhlIHNtYWxsZXIgdGhlIGJldHRlci4NCg0KVGhlIGJlc3QgbW9kZWwgaGFzIGJlZW4gdGhlIGBuYWl2ZWAgd2l0aCBhICpNQVBFKiBvZiAyNi45LCBmb2xsb3dlZCBieSB0aGUgdHdvIHZhcmlhbnRzIG9mDQp0aGUgYHNuYWl2ZWAuIEFuIGVycm9yIG9mIGEgKk1BUEUqIG9mIDI2LjkgbWVhbnMgdGhhdCB0aGUgbW9kZWwgaGFzIGFuIGVycm9yIG9mIDI2LjklLg0KDQpgYGB7ciBmb3JlY2F0LWF1eC1mdW5jdGlvbnN9DQojIERlZmluZSB0aGUgbWV0cmljcyB0aGF0IHdlIHdhbnQgdG8gdXNlIHdoZW4gDQojIGV2YWx1YXRpbmcgdGhlIGZvcmVjYXN0cw0KbWVhc3VyZXMgPC0gbGlzdChSTVNTRSA9IFJNU1NFLCBNQVBFID0gTUFQRSwgQ1JQUyA9IENSUFMpDQoNCiMgRnVuY3Rpb24gdG8gcmFuayB0aGUgbW9kZWxzIGJhc2VkIG9uIGEgbWF0cmljDQphZGRfcmFuayA8LSBmdW5jdGlvbiguZGF0YSwgdmFyID0gTlVMTCkgew0KICB2YXIgPC0gc3ltKHZhcikNCiAgLmRhdGEgJT4lIA0KICAgIGdyb3VwX2J5KGlkKSAlPiUgDQogICAgbXV0YXRlKHJhbmsgPSBkZW5zZV9yYW5rKCEhdmFyKSkgJT4lIA0KICAgIHVuZ3JvdXAoKSAlPiUgDQogICAgYXJyYW5nZShpZCwgISF2YXIpDQp9DQpgYGANCg0KYGBge3IgYmFzZWxpbmUtYWNjdXJhY3l9DQojIENhbGN1bGF0ZSBhY2N1cmFjeSBmb3IgZWFjaCBzZXJpZXMgYW5kIG1vZGVsDQphY2NfYmFzZWxpbmVzIDwtIGZjX2Jhc2VsaW5lcyAlPiUgDQogIGFjY3VyYWN5KGJpbmRfcm93cyh0cmFpbl9kYXRhLCB2YWxpZF9kYXRhKSwgbWVhc3VyZXMpICU+JSANCiAgYWRkX3JhbmsodmFyID0gIk1BUEUiKQ0KDQojIEFnZ3JlZ2F0ZSBtZXRyaWNzIGJ5IGAubW9kZWxgDQphY2NfYmFzZWxpbmVzICU+JSANCiAgZ3JvdXBfYnkoLm1vZGVsKSAlPiUgDQogIHN1bW1hcmlzZV9hdCh2YXJzKFJNU1NFOkNSUFMpLCBtZWFuKSAlPiUgDQogIGFycmFuZ2UoTUFQRSkgJT4lIA0KICB0b19odG1sKGRpZ2l0cyA9IDEsIGZ1bGxfd2lkdGggPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KIyAjIEJ1c2NhbW9zIGRpZmVyZW5jaWFzIGVudHJlIG1vZGVsb3MgcGFyYSBtb3N0cmFybG8gZW4gdmlzdWFsaXphY2lvbmVzDQojIGFjY19iYXNlbGluZXMgJT4lIA0KIyAgIHBpdm90X3dpZGVyKGlkX2NvbHMgPSBpZCwgbmFtZXNfZnJvbSA9IC5tb2RlbCwgdmFsdWVzX2Zyb20gPSBNQVBFKSAlPiUgDQojICAgbXV0YXRlKA0KIyAgICAgZGlmZl8xID0gYWJzKG5haXZlIC0gc25haXZlXzEpLA0KIyAgICAgZGlmZl8yID0gYWJzKHNuYWl2ZV8xIC0gc25haXZlXzIpDQojICAgKSAlPiUgDQojICAgYXJyYW5nZShkZXNjKGRpZmZfMikpICU+JSANCiMgICBoZWFkKCkNCmBgYA0KDQpOb3cgdGhhdCB3ZSBoYXZlIHRoZSBmb3JlY2FzdCBhbmQgd2Uga25vdyB3aGF0IGhhcyBiZWVuIHRoZSBiZXN0IG1vZGVsLCBsZXQncyB2aXN1YWxpemUNCnRoZSBmb3JlY2FzdCB0byBnZXQgaW5zaWdodCBpbnRvIHRoZSBkYXRhLg0KDQpgYGB7ciBhdXgtZnVuY3Rpb24tdml6LWZvcmVjYXN0c30NCnBsb3RfZm9yZWNhc3QgPC0gDQogIGZ1bmN0aW9uKGZjLCBkYXRhID0gYmluZF9yb3dzKHRyYWluX2RhdGEsIHZhbGlkX2RhdGEpLCANCiAgICAgICAgICAgZmFjZXRzID0gTlVMTCwgbGV2ZWwgPSA4MCwgbmNvbCA9IE5VTEwsIHNjYWxlcyA9ICJmaXhlZCIsIA0KICAgICAgICAgICB0aXRsZSA9IE5VTEwpIHsNCiAgICANCiAgICBpZiAoaXMubnVsbCh0aXRsZSkpIHsNCiAgICAgIHRpdGxlIDwtIHNwcmludGYoIkZvcmVjYXN0IElEOiAlcyIsIGZjJGlkW1sxXV0pDQogICAgfQ0KICAgIA0KICAgIGdnIDwtIGZjICU+JSANCiAgICAgIGF1dG9wbG90KGRhdGEsIGxldmVsID0gbGV2ZWwsIHNpemUgPSAwLjgpICsNCiAgICAgICMgZ3VpZGVzKGNvbG91ciA9IEZBTFNFKSArDQogICAgICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgICAgIGxhYmVscyA9IHNjYWxlczo6bGFiZWxfbnVtYmVyKHN1ZmZpeCA9ICJLIiwgc2NhbGUgPSAxZS0zKQ0KICAgICAgKSArDQogICAgICBsYWJzKHN1YnRpdGxlID0gdGl0bGUsIHggPSBOVUxMLCB5ID0gTlVMTCwgY29sb3VyID0gIk1vZGVsIikgKw0KICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgICAgIGdndGhlbWVzOjpzY2FsZV9jb2xvcl90YWJsZWF1KHBhbGV0dGUgPSAiQ2xhc3NpYyAxMCBNZWRpdW0iKQ0KICAgIA0KICAgIGlmICghaXMubnVsbChuY29sKSkgew0KICAgICAgZ2cgPC0gZ2cgKyANCiAgICAgICAgZmFjZXRfd3JhcChmYWNldHMsIG5jb2wgPSBuY29sLCBzY2FsZXMgPSBzY2FsZXMpDQogICAgfQ0KICAgIGdnDQp9DQpgYGANCg0KQWx0aG91Z2ggdGhlIGBzbmFpdmVgIG1vZGVsIGhhcyBwZXJmb3JtZWQgYmV0dGVyIG92ZXJhbGwsIGluIHNvbWUgc2VyaWVzIGl0IHBlcmZvcm1zIHdvcnNlDQp0aGFuIHRoZSBgc25haXZlYC4NCg0KYGBge3Igdml6LWJhc2VsaW5lLWRpZmYtbmFpdmV9DQojIEJldHRlciBzbmFpdmUgdGhhbiBuYWl2ZQ0KZmNfYmFzZWxpbmVzICU+JSANCiAgZmlsdGVyKGlkID09ICJDbHVzdGVyIDdfX0JyYW5kIEdyb3VwIDMwIikgJT4lDQogIHBsb3RfZm9yZWNhc3QoZmFjZXRzID0gIi5tb2RlbCIsIG5jb2wgPSBOVUxMLCBsZXZlbCA9IE5VTEwsIHRpdGxlID0gIiIpICsNCiAgZmFjZXRfd3JhcCh+IGlkKQ0KYGBgDQoNCldlIGFsc28gZmluZCBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBgc25haXZlYCBtb2RlbHMuIEluIHRoZSBmb2xsb3dpbmcgcGxvdCB3ZSBjYW4gc2VlIGhvdw0KdGhlIGBzbmFpdmVgIHdpdGggZHJpZnQgaXMgdW5kZXItZm9yZWNhc3Rpbmcgd2hpbGUgdGhlIG90aGVyLCB3aXRob3V0IGRyaWZ0LCBpcyBwcm92aWRpbmcNCmFuIGFjY2VwdGFibGUgZm9yZWNhc3QuDQoNCmBgYHtyIHZpei1iYXNlbGluZS1kaWZmLXNuYWl2ZX0NCmZjX2Jhc2VsaW5lcyAlPiUgDQogIGZpbHRlcihpZCA9PSAiQ2x1c3RlciAyX19PdGhlcnMiLCANCiAgICAgICAgIC5tb2RlbCAlaW4lIGMoInNuYWl2ZV8xIiwgInNuYWl2ZV8yIikpICU+JQ0KICBwbG90X2ZvcmVjYXN0KGZhY2V0cyA9ICIubW9kZWwiLCBuY29sID0gTlVMTCwgbGV2ZWwgPSBOVUxMLCB0aXRsZSA9ICIiKSArDQogIGZhY2V0X3dyYXAofiBpZCkNCmBgYA0KDQpTb21ldGltZXMgbm8gbW9kZWwgaXMgZ29vZC4NCg0KYGBge3Igdml6LW5haXZlLWFsbC13cm9uZ30NCmZjX2Jhc2VsaW5lcyAlPiUgDQogIGZpbHRlcihpZCA9PSAiQ2x1c3RlciA1X19CcmFuZCBHcm91cCAzMCIpICU+JQ0KICBwbG90X2ZvcmVjYXN0KGZhY2V0cyA9ICIubW9kZWwiLCBuY29sID0gTlVMTCwgbGV2ZWwgPSBOVUxMLCB0aXRsZSA9ICIiKSArDQogIGZhY2V0X3dyYXAofiBpZCkNCmBgYA0KDQojIyBTdGF0aXN0aWNhbCBCZW5jaG1hcmtzIHsjc3RhdGlzdGljYWwtYmVuY2htYXJrc30NCg0KRXhwb25lbnRpYWwgc21vb3RoaW5nIGFuZCBBUklNQSBtb2RlbHMgYXJlIHRoZSB0d28gbW9zdCB3aWRlbHkgdXNlZCBhcHByb2FjaGVzIHRvIHRpbWUNCnNlcmllcyBmb3JlY2FzdGluZywgYW5kIHByb3ZpZGUgY29tcGxlbWVudGFyeSBhcHByb2FjaGVzIHRvIHRoZSBwcm9ibGVtLiBXaGlsZSBleHBvbmVudGlhbA0Kc21vb3RoaW5nIG1vZGVscyBhcmUgYmFzZWQgb24gYSBkZXNjcmlwdGlvbiBvZiB0aGUgdHJlbmQgYW5kIHNlYXNvbmFsaXR5IGluIHRoZSBkYXRhLA0KQVJJTUEgbW9kZWxzIGFpbSB0byBkZXNjcmliZSB0aGUgYXV0b2NvcnJlbGF0aW9ucyBpbiB0aGUgZGF0YS4NCg0KLSAgIGBBUklNQWA6IGF1dG9yZWdyZXNzaXZlIGludGVncmF0ZWQgbW92aW5nIGF2ZXJhZ2UgbW9kZWwNCi0gICBgRVRTYDogZXhwb25lbnRpYWwgc21vb3RoaW5nIG1vZGVscyBhcmUgYmFzZWQgb24gYSBkZXNjcmlwdGlvbiBvZiB0aGUgdHJlbmQgYW5kDQogICAgc2Vhc29uYWxpdHkgaW4gdGhlIGRhdGENCg0KV2hlbiB0aGVyZSBhcmUgbG9uZyBzZWFzb25hbCBwZXJpb2RzLCBhIGR5bmFtaWMgcmVncmVzc2lvbiB3aXRoIGZvdXJpZXIgdGVybXMgaXMgb2Z0ZW4NCmJldHRlciB0aGFuIGBBUklNQWAgYW5kIGBFVFNgIG1vZGVscy4NCg0KRm9yIGV4YW1wbGUsIGRhaWx5IGRhdGEgY2FuIGhhdmUgYW5udWFsIHNlYXNvbmFsaXR5IG9mIGxlbmd0aCAzNjUsIHdlZWtseSBkYXRhIGhhcw0Kc2Vhc29uYWwgcGVyaW9kIG9mIGFwcHJveGltYXRlbHkgNTIsIHdoaWxlIGhhbGYtaG91cmx5IGRhdGEgY2FuIGhhdmUgc2V2ZXJhbCBzZWFzb25hbA0KcGVyaW9kcywgdGhlIHNob3J0ZXN0IG9mIHdoaWNoIGlzIHRoZSBkYWlseSBwYXR0ZXJuIG9mIHBlcmlvZCA0OC4NCg0KRGVzcGl0ZSB3ZSBkbyBub3QgaGF2ZSBsb25nIHNlYXNvbmFsIHBlcmlvZHMgaGVyZSwgdGhpcyBtb2RlbHMgd2lsbCBiZSBpbmNsdWRlZCBpbiBvdXINCmxpc3Qgb2YgbW9kZWxzLg0KDQotICAgYEZPVVJJRVJgOiBMaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB3aXRoIGVycm9ycyBtb2RlbGVkIHdpdGggYW4gYXJpbWEgbW9kZWwgdG8gcmVtb3ZlDQogICAgdGhlIGF1dG9jb3JyZWxhdGlvbi4NCg0KQWRkaXRpb25hbGx5IHRvIHRoaXMgbW9kZWxzLCBpdCBoYXMgYWxzbyBiZWVuIGluY2x1ZGVkIGEgY29tYmluYXRpb24gZm9yZWNhc3Qgb2YgYEFSSU1BYA0KYW5kIGBFVFNgIG1vZGVscyB3aGljaCBoYXMgYmVlbiBjYWxsZWQgYGNvbWJuYC4NCg0KYGBge3Igc3RhdGlzdGljYWwtZGVmaW5pdGlvbiwgZWNobz1UUlVFfQ0KbW9kZWxzX3N0YXQgPC0gbGlzdCgNCiAgIyBTdGF0aXN0aWNhbCBiZW5jaG1hcmtzDQogIGFyaW1hID0gQVJJTUEoc2FsZXNfMiksDQogIGV0cyAgID0gRVRTKHNhbGVzXzIpLA0KICANCiAgIyBVc2luZyBGb3VyaWVyIHRlcm1zIGFuZCBBUklNQSBlcnJvcnMgZm9yIGZvcmVjYXN0aW5nDQogIGBLID0gMWAgPSBBUklNQShzYWxlc18yIH4gZm91cmllcihLID0gMSkgKyBQRFEoMCwgMCwgMCkpLA0KICBgSyA9IDJgID0gQVJJTUEoc2FsZXNfMiB+IGZvdXJpZXIoSyA9IDIpICsgUERRKDAsIDAsIDApKSwNCiAgYEsgPSAzYCA9IEFSSU1BKHNhbGVzXzIgfiBmb3VyaWVyKEsgPSAzKSArIFBEUSgwLCAwLCAwKSksDQogIGBLID0gNGAgPSBBUklNQShzYWxlc18yIH4gZm91cmllcihLID0gNCkgKyBQRFEoMCwgMCwgMCkpLA0KICBgSyA9IDVgID0gQVJJTUEoc2FsZXNfMiB+IGZvdXJpZXIoSyA9IDUpICsgUERRKDAsIDAsIDApKSwNCiAgYEsgPSA2YCA9IEFSSU1BKHNhbGVzXzIgfiBmb3VyaWVyKEsgPSA2KSArIFBEUSgwLCAwLCAwKSkNCikNCmBgYA0KDQpgYGB7ciBmaXQtc3RhdGlzdGljYWwtbW9kZWxzLCBldmFsPWlzX3RyYWluX21vZGVsc30NCiMgVHJhaW4gbW9kZWxzDQpmaXRfc3RhdGlzdGljYWwgPC0gdHJhaW5fZGF0YSAlPiUgDQogIG1vZGVsKCEhIW1vZGVsc19zdGF0KSAlPiUgDQogIG11dGF0ZShjb21ibiA9IChhcmltYSArIGV0cykgLyAyKQ0KDQojIFNhdmUgbW9kZWxzDQp3cml0ZV9yZHMoZml0X3N0YXRpc3RpY2FsLCANCiAgZmlsZS5wYXRoKHBhdGhfbW9kZWxzLCAiZml0X3N0YXRpc3RpY2FsLnJkcyIpLCANCiAgY29tcHJlc3MgPSAiZ3oiKQ0KDQojIEdlbmVyYXRlIGZvcmVjYXN0cyBmb3IgdGhlIHZhbGlkYXRpb24gcGVyaW9kDQpmY19zdGF0aXN0aWNhbCA8LSBmaXRfc3RhdGlzdGljYWwgJT4lIA0KICBmb3JlY2FzdChuZXdfZGF0YSA9IHZhbGlkX2RhdGEpDQoNCiMgU2F2ZSBmb3JlY2FzdHMNCndyaXRlX3JkcyhmY19zdGF0aXN0aWNhbCwgDQogIGZpbGUucGF0aChwYXRoX21vZGVscywgImZjX3N0YXRpc3RpY2FsLnJkcyIpLCANCiAgY29tcHJlc3MgPSAiZ3oiKQ0KYGBgDQoNCmBgYHtyIHN0YXRpc3RpY2FsLWZvcmVjYXN0LWdlbmVyYXRpb259DQojIExvYWQgbW9kZWxzDQpmaXRfc3RhdGlzdGljYWwgPC0gcmVhZF9yZHMoZmlsZS5wYXRoKHBhdGhfbW9kZWxzLCAiZml0X3N0YXRpc3RpY2FsLnJkcyIpKQ0KDQojIExvYWQgZm9yZWNhc3RzDQpmY19zdGF0aXN0aWNhbCA8LSByZWFkX3JkcyhmaWxlLnBhdGgocGF0aF9tb2RlbHMsICJmY19zdGF0aXN0aWNhbC5yZHMiKSkNCg0KIyBDYWxjdWxhdGUgYWNjdXJhY3kgZm9yIGVhY2ggc2VyaWVzIGFuZCBtb2RlbA0KYWNjX3N0YXRpc3RpY2FsIDwtIGZjX3N0YXRpc3RpY2FsICU+JSANCiAgYWNjdXJhY3koYmluZF9yb3dzKHRyYWluX2RhdGEsIHZhbGlkX2RhdGEpLCBtZWFzdXJlcykgJT4lIA0KICBhZGRfcmFuayh2YXIgPSAiTUFQRSIpDQpgYGANCg0KVGhlIGJlc3QgbW9kZWwgaXMgdGhlIGBjb21ibmAsIGZvbGxvd2VkIGJ5IHRoZSBgQVJJTUFgIGFuZCB0aGUgYEsgPSAyYCBtb2RlbHMuIFRoZSBlcnJvcg0KaGFzIGJlZW4gcmVkdWNlZCBieSAyNi40JSB3aXRoIHJlc3BlY3QgdG8gdGhlIGBuYWl2ZWAgbW9kZWwuDQoNCmBgYHtyIG1vZGVsLXJlZHVjdGlvbi1wZXJjZW50YWdlLCBlY2hvPUZBTFNFLCBldmFsPUZBTFNFfQ0Kbm4gPC0gMjYuOSAgICAjIG5haXZlIG1vZGVsDQpjYyA8LSAxOS44ICAgICMgY29tYm4gbW9kZWwNCihjYyAvIG5uKSAtIDEgIyByZWR1Y3Rpb24gcGVyY2VudGFnZQ0KIyBbMV0gLTAuMjYzOTQwNQ0KYGBgDQoNCmBgYHtyIHN0YXRpc3RpY2FsLWFjY3VyYWN5fQ0KIyBBZ2dyZWdhdGUgbWV0cmljcyBieSBgLm1vZGVsYA0KYWNjX3N0YXRpc3RpY2FsICU+JSANCiAgZ3JvdXBfYnkoLm1vZGVsKSAlPiUgDQogIHN1bW1hcmlzZV9hdCh2YXJzKFJNU1NFOkNSUFMpLCBtZWFuKSAlPiUgDQogIGFycmFuZ2UoTUFQRSkgJT4lIA0KICBoZWFkKDYpICU+JSANCiAgdG9faHRtbChkaWdpdHMgPSAxKQ0KYGBgDQoNCmBgYHtyfQ0KIyAjIERvZXMgYWxsIHRoZSBicmFuZHMgcGVyZm9ybSBlcXVhbGx5PyBJcyBhbHdheXMgdGhlIHNhbWUgbW9kZWwgdGhlIGJlc3QgcGVyZm9ybWVyPw0KIyBhY2Nfc3RhdGlzdGljYWwgJT4lIA0KIyAgIHNlcGFyYXRlKGlkLCBpbnRvID0gYygiY2x1c3RlciIsICJicmFuZCIpLCBzZXAgPSAiX18iKSAlPiUgDQojICAgZ3JvdXBfYnkoYnJhbmQsIC5tb2RlbCkgJT4lIA0KIyAgIHN1bW1hcmlzZV9hdCh2YXJzKFJNU1NFOkNSUFMpLCBtZWFuKSAlPiUgDQojICAgYXJyYW5nZShNQVBFKSAlPiUgDQojICAgc2xpY2UoMSkgJT4lIA0KIyAgIHRvX2h0bWwoZGlnaXRzID0gMSkNCmBgYA0KDQpJbiB0aGUgZXhhbXBsZSBiZWxvdywgbmVpdGhlciBtb2RlbCBoYXMgZ2VuZXJhdGVkIGEgZ29vZCBmb3JlY2FzdCwgaW4gZmFjdCwgaXQgc2VlbXMgdGhhdA0KdGhlIGBldHNgIG1vZGVsIG9ubHkgaGFzIGNhcHR1cmVkIHRoZSB0cmVuZC4NCg0KYGBge3Igdml6LXN0YXQtZm9yZWNhc3QtMDF9DQpmY19zdGF0aXN0aWNhbCAlPiUNCiAgZmlsdGVyKA0KICAgIGlkID09ICJDbHVzdGVyIDdfX0JyYW5kIEdyb3VwIDMwIiwNCiAgICAubW9kZWwgJWluJSBjKCJhcmltYSIsICJldHMiKQ0KICApICU+JQ0KICBwbG90X2ZvcmVjYXN0KGZhY2V0cyA9ICIubW9kZWwiLCBuY29sID0gTlVMTCwgbGV2ZWwgPSBOVUxMLCB0aXRsZSA9ICIiKSArDQogIGZhY2V0X3dyYXAofiBpZCkNCmBgYA0KDQpJbiB0aGlzIG90aGVyIGNhc2UsIGJvdGggZm9yZWNhc3RzIGFyZSBzaW1pbGFyIHRvIGVhY2ggb3RoZXIgYW5kIHRoZSBgc25haXZlXzFgDQoNCmBgYHtyIHZpei1zdGF0LWZvcmVjYXN0LTAyfQ0KZmNfc3RhdGlzdGljYWwgJT4lIA0KICBmaWx0ZXIoDQogICAgaWQgPT0gIkNsdXN0ZXIgMl9fT3RoZXJzIiwgDQogICAgLm1vZGVsICVpbiUgYygiZXRzIiwgImFyaW1hIikNCiAgKSAlPiUNCiAgcGxvdF9mb3JlY2FzdChmYWNldHMgPSAiLm1vZGVsIiwgbmNvbCA9IE5VTEwsIGxldmVsID0gTlVMTCwgdGl0bGUgPSAiIikgKw0KICBmYWNldF93cmFwKH4gaWQpDQpgYGANCg0KQWdhaW4sIG5vbmUgb2YgdGhlIG1vZGVscyBoYXMgYmVlbiBhYmxlIHRvIGNhcHR1cmUgdGhlIGJpZyBkcm9wIG9mZiBvZiAyMDE3Lg0KDQpgYGB7ciB2aXotc3RhdGlzdGljYWwtYWxsLXdyb25nfQ0KZmNfc3RhdGlzdGljYWwgJT4lIA0KICBmaWx0ZXIoDQogICAgaWQgPT0gIkNsdXN0ZXIgNV9fQnJhbmQgR3JvdXAgMzAiDQogICkgJT4lDQogIHBsb3RfZm9yZWNhc3QoZmFjZXRzID0gIi5tb2RlbCIsIG5jb2wgPSBOVUxMLCBsZXZlbCA9IE5VTEwsIHRpdGxlID0gIiIpICsNCiAgZmFjZXRfd3JhcCh+IGlkKQ0KYGBgDQoNCiMjIEV4dGVybmFsIEluZm9ybWF0aW9uDQoNClRoZSB0aW1lIHNlcmllcyBtb2RlbHMgdXNlZCBzbyBmYXIgYWxsb3cgZm9yIHRoZSBpbmNsdXNpb24gb2YgaW5mb3JtYXRpb24gZnJvbSBwYXN0DQpvYnNlcnZhdGlvbnMgb2YgYSBzZXJpZXMsIGJ1dCBub3QgZm9yIHRoZSBpbmNsdXNpb24gb2Ygb3RoZXIgaW5mb3JtYXRpb24gdGhhdCBtYXkgYWxzbyBiZQ0KcmVsZXZhbnQuIEZvciBleGFtcGxlLCB0aGUgZWZmZWN0cyBvZiB0aGUgaW52ZXN0bWVudHMgbWF5IGV4cGxhaW4gc29tZSBvZiB0aGUgaGlzdG9yaWNhbA0KdmFyaWF0aW9uIGFuZCBtYXkgbGVhZCB0byBtb3JlIGFjY3VyYXRlIGZvcmVjYXN0cw0KDQpgYGB7ciB4cmVnLWRlZmluaXRpb24sIGVjaG89VFJVRX0NCiMgVXNpbmcgaW52ZXN0bWVudHMgYXMgcHJlZGljdG9ycw0KbW9kZWxzX3hyZWcgPC0gbGlzdCgNCiAgeHJlZ18xID0gQVJJTUEoc2FsZXNfMiB+IGludmVzdG1lbnRfMSksDQogIHhyZWdfMiA9IEFSSU1BKHNhbGVzXzIgfiBpbnZlc3RtZW50XzEgKyBpbnZlc3RtZW50XzIpLA0KICB4cmVnXzMgPSBBUklNQShzYWxlc18yIH4gaW52ZXN0bWVudF8xICsgaW52ZXN0bWVudF8yICsgaW52ZXN0bWVudF8zKSwNCiAgeHJlZ180ID0gQVJJTUEoc2FsZXNfMiB+IGludmVzdG1lbnRfMSArIGludmVzdG1lbnRfMiArIGludmVzdG1lbnRfMyArIA0KICAgICAgICAgICAgICAgICAgIGludmVzdG1lbnRfNCArIGludmVzdG1lbnRfNSArIGludmVzdG1lbnRfNikNCikNCmBgYA0KDQpgYGB7ciB0cmFpbi1tb2RlbHMteHJlZywgZXZhbD1pc190cmFpbl9tb2RlbHN9DQojIFRyYWluIG1vZGVscw0KZml0X3hyZWdfIDwtIHRyYWluX2RhdGEgJT4lIA0KICBtb2RlbCghISFtb2RlbHNfeHJlZykNCg0KIyBBcHBlbmQgQVJJTUEgbW9kZWxzIGFzIGRlZmF1bHQgaW4gY2FzZSBvZiBmYWlsdXJlDQojIFJlcGxhY2UgTlVMTCBtb2RlbHMgYnkgYSBgZGVmYXVsdGAgbW9kZWwNCmZpdF94cmVnIDwtIGZpdF94cmVnXyAlPiUgDQogIGxlZnRfam9pbigNCiAgICBzZWxlY3QoZml0X3N0YXRpc3RpY2FsLCBpZCwgZGVmYXVsdCA9IGFyaW1hKSwgDQogICAgYnkgPSAiaWQiDQogICkgJT4lIA0KICBtdXRhdGUoYWNyb3NzKGNvbnRhaW5zKCJ4cmVnIiksIH4gaWZfZWxzZShpc19udWxsX21vZGVsKC54KSwgZGVmYXVsdCwgLngpICkpICU+JSANCiAgIyBEcm9wIGRlZmF1bHQgbW9kZWwNCiAgc2VsZWN0KC1kZWZhdWx0KQ0KDQojIFNhdmUgbW9kZWxzDQp3cml0ZV9yZHMoZml0X3hyZWcsIA0KICBmaWxlLnBhdGgocGF0aF9tb2RlbHMsICJmaXRfeHJlZy5yZHMiKSwgDQogIGNvbXByZXNzID0gImd6IikNCg0KIyBHZW5lcmF0ZSBmb3JlY2FzdHMgZm9yIHRoZSB2YWxpZGF0aW9uIHBlcmlvZA0KZmNfeHJlZyA8LSBmaXRfeHJlZyAlPiUgDQogIGZvcmVjYXN0KG5ld19kYXRhID0gdmFsaWRfZGF0YSkNCg0KIyBTYXZlIGZvcmVjYXN0cw0Kd3JpdGVfcmRzKGZjX3hyZWcsIA0KICBmaWxlLnBhdGgocGF0aF9tb2RlbHMsICJmY194cmVnLnJkcyIpLCANCiAgY29tcHJlc3MgPSAiZ3oiKQ0KYGBgDQoNCmBgYHtyIGxvYWQteHJlZy1tb2RlbHN9DQojIExvYWQgbW9kZWxzDQpmaXRfeHJlZyA8LSByZWFkX3JkcyhmaWxlLnBhdGgocGF0aF9tb2RlbHMsICJmaXRfeHJlZy5yZHMiKSkNCg0KIyBMb2FkIGZvcmVjYXN0cw0KZmNfeHJlZyA8LSByZWFkX3JkcyhmaWxlLnBhdGgocGF0aF9tb2RlbHMsICJmY194cmVnLnJkcyIpKQ0KDQojIENhbGN1bGF0ZSBhY2N1cmFjeSBmb3IgZWFjaCBzZXJpZXMgYW5kIG1vZGVsDQphY2NfeHJlZyA8LSBmY194cmVnICU+JSANCiAgYWNjdXJhY3koYmluZF9yb3dzKHRyYWluX2RhdGEsIHZhbGlkX2RhdGEpLCBtZWFzdXJlcykgJT4lIA0KICBhZGRfcmFuayh2YXIgPSAiTUFQRSIpDQpgYGANCg0KT3ZlcmFsbCBhY2N1cmFjeSBpcyBub3QgYXMgZ29vZCBhcyBgY29tYm5gLCBgYXJpbWFgIG9yYGV0c2AgbW9kZWxzIHRyYWluZWQgb24gW3N0YXRpc3RpY2FsDQpiZW5jaG1hcmtzXSgjc3RhdGlzdGljYWwtYmVuY2htYXJrcykgc2VjdGlvbi4NCg0KYGBge3IgYWNjLXhyZWctbW9kZWxzfQ0KIyBBZ2dyZWdhdGUgbWV0cmljcyBieSBgLm1vZGVsYA0KYWNjX3hyZWcgJT4lIA0KICBncm91cF9ieSgubW9kZWwpICU+JSANCiAgc3VtbWFyaXNlX2F0KHZhcnMoUk1TU0U6Q1JQUyksIG1lYW4pICU+JSANCiAgYXJyYW5nZShNQVBFKSAlPiUgDQogIHRvX2h0bWwoZGlnaXRzID0gMSkNCmBgYA0KDQpNb2RlbCBgSyA9IDRgIG92ZXJmaXQgdGhlIGRhdGEgYW5kIGdlbmVyYXRlIGEgZm9yZWNhc3QgaW4gdGhlIG9wcG9zaXRlIGRpcmVjdGlvbi4gSW4NCmNvbnRyYXN0LCB0aGUgYEsgPSAyYCBpcyBhYmxlIHRvIGNhcHR1cmUgYSBtb3JlIGdlbmVyYWwgcGF0dGVybi4gVGhpcyBpcyBhIGdvb2QgZXhhbXBsZQ0KdGhhdCBtb3JlIGNvbXBsZXggbW9kZWxzIGRvIG5vdCBhbHdheXMgaW5kaWNhdGUgYmV0dGVyIHJlc3VsdHMuDQoNCmBgYHtyIHZpei14cmVnLW1vZGVscy0wMX0NCmZjX3hyZWcgJT4lDQogIGZpbHRlcigNCiAgICBpZCA9PSAiQ2x1c3RlciA3X19CcmFuZCBHcm91cCAzMCIsDQogICAgLm1vZGVsICVpbiUgYygieHJlZ18yIiwgInhyZWdfNCIpDQogICkgJT4lDQogIHBsb3RfZm9yZWNhc3QoZmFjZXRzID0gIi5tb2RlbCIsIG5jb2wgPSBOVUxMLCBsZXZlbCA9IE5VTEwsIHRpdGxlID0gIiIpICsNCiAgbGFicyh0aXRsZSA9IE5VTEwpICsNCiAgZmFjZXRfd3JhcCh+IGlkKQ0KYGBgDQoNCkFsbCB0aGUgZm9yZWNhc3RzIGFyZSBxdWl0ZSBzaW1pbGFyIGluIHRoaXMgY2FzZQ0KDQpgYGB7ciB2aXoteHJlZy1tb2RlbHMtMDJ9DQpmY194cmVnICU+JSANCiAgZmlsdGVyKGlkID09ICJDbHVzdGVyIDJfX090aGVycyIpICU+JQ0KICBwbG90X2ZvcmVjYXN0KGZhY2V0cyA9ICIubW9kZWwiLCBuY29sID0gTlVMTCwgbGV2ZWwgPSBOVUxMLCB0aXRsZSA9ICIiKSArDQogIGZhY2V0X3dyYXAofiBpZCkNCmBgYA0KDQpBcHBhcmVudGx5LCB0aGUgaW52ZXN0bWVudHMgbmVpdGhlciBleHBsYWluIHRoZSBkcm9wIG9mZiBvZiAyMDE3Lg0KDQpgYGB7ciB2aXoteHJlZy1tb2RlbHMtMDN9DQpmY194cmVnICU+JSANCiAgZmlsdGVyKGlkID09ICJDbHVzdGVyIDVfX0JyYW5kIEdyb3VwIDMwIikgJT4lDQogIHBsb3RfZm9yZWNhc3QoZmFjZXRzID0gIi5tb2RlbCIsIG5jb2wgPSBOVUxMLCBsZXZlbCA9IE5VTEwsIHRpdGxlID0gIiIpICsNCiAgZmFjZXRfd3JhcCh+IGlkKQ0KYGBgDQoNCkZvciBjYXNlcyBsaWtlIHRoaXMsIHdlIGFyZSBnb2luZyB0byBpbnRyb2R1Y2UgYSBuZXcgc2V0IG9mIG1vZGVscyB0aGF0IHdpbGwgaGVscCB1cyB0bw0KaW1wcm92ZSB0aGlzIHN0cnVjdHVyYWwgY2hhbmdlcy4NCg0KYGBge3IgZnV0dXJlLXBsYW4tc2VxdWVudGlhbH0NCmZ1dHVyZTo6cGxhbigic2VxdWVudGlhbCIpDQpgYGANCg0KIyMgUGllY2V3aXNlIHJlZ3Jlc3Npb24NCg0KV2UgaGF2ZSBzZWVuIHRoYXQsIGluIHNvbWUgY2FzZXMsIHRoZSB0cmVuZCBpcyBub3QgbGluZWFyIGFuZCB0aGUgbW9kZWxzIGFyZSBub3QgYWJsZSB0bw0KbW9kZWwgdGhlIHNlcmllcyBjb3JyZWN0bHkuIFBpZWNlLXdpc2UgbW9kZWxzIGRpdmlkZSB0aGUgc2VyaWVzIGludG8gc2V2ZXJhbCBzZWdtZW50cyBpbg0Kc3VjaCBhIHdheSB0aGF0IHRoZXkgYXJlIGFibGUgdG8gZGV0ZWN0IHRoZSB0cmVuZCBjb3JyZWN0bHkuDQoNCiMjIyBCcmFuZCBhbmFseXNpcw0KDQpQZXJmb3JtaW5nIHRoZSBhbmFseXNpcyBmb3IgZWFjaCBzZXJpZXMgaXMgYSBjb21wbGljYXRlZCBhbmQgdGltZS1jb25zdW1pbmcgdGFzay4gRm9yIHRoaXMNCnJlYXNvbiwgbWFueSB0aW1lcyBoaWdoZXIgbGV2ZWxzIG9mIGFnZ3JlZ2F0aW9uIGFyZSBhbmFseXplZCBhbmQgdGhlbiB0aGVzZSBjb25kaXRpb25zIG9yDQpwYXJhbWV0ZXJzIGFyZSBhcHBsaWVkIHRvIGxvd2VyIGxldmVscyBzZXJpZXMuDQoNCmBgYHtyIGJyYW5kLXRpYmJsZS10by10c2liYmxlfQ0KYnlfYnJhbmRfdHNpYmJsZSA8LSBieV9icmFuZCAlPiUgDQogIGZpbHRlcihkYXRlIDwgYXMuRGF0ZShsYXN0X3RyYWluKSkgJT4lIA0KICBtdXRhdGUoZGF0ZSA9IHRzaWJibGU6OnllYXJtb250aCghIXN5bShpbmRleCkpKSAlPiUNCiAgYXNfdHNpYmJsZShrZXkgPSBicmFuZCwgaW5kZXggPSBkYXRlKQ0KYGBgDQoNClRoZSBwbG90IG9mIHRoZSAqQnJhbmQgR3JvdXAgMzEqIHJldmVhbHMgdHdvIGRpZmZlcmVudCBwZXJpb2RzLiBUaGVyZSBpcyBhIGNsZWFyIHVwdHJlbmQNCnVwIHRvIHRoZSBlbmQgb2YgMjAxNS4gQWZ0ZXIgMjAxNSB0aGVyZSBpcyBhIGRlY3JlYXNlIGluIHRoZSBncm93aW5nIHNwZWVkIGFuZCB0aGUgdHJlbmQNCnNsb3cgZG93biBhIGJpdC4gVG8gYWNjb3VudCBmb3IgdGhlc2UgY2hhbmdlcywgd2Ugc3BlY2lmeSB0aGUgZGF0ZSAqMjAxNS0wMSogYXMga25vdHMuDQoNCmBgYHtyIGZpdC1tb2RlbC1icmFuZC0zMSwgZWNobz1UUlVFfQ0KIyBTZWxlY3QgYSBicmFuZCBhIGZpdCBhIG1vZGVsDQpjdXJyZW50X2JyYW5kIDwtICJCcmFuZCBHcm91cCAzMSINCg0KIyBGaXQgc2ltcGxlIGBsbWAgYW5kIGBwaWVjZXdpc2VgIG1vZGVsDQpmaXRfdHJlbmRzIDwtIGJ5X2JyYW5kX3RzaWJibGUgJT4lIA0KICBmaWx0ZXIoYnJhbmQgPT0gY3VycmVudF9icmFuZCkgJT4lIA0KICBtb2RlbCgNCiAgICBsbSA9IFRTTE0oc2FsZXNfMiB+IHRyZW5kKCkpLA0KICAgIHBpZWNld2lzZSA9IFRTTE0oc2FsZXNfMiB+IHRyZW5kKA0KICAgICAga25vdHMgPSB0c2liYmxlOjp5ZWFybW9udGgoIjIwMTUtMDEiKSkpDQogICkNCmBgYA0KDQpgYGB7ciB2aXotdHNsbS10cmVuZH0NCmZjX3RyZW5kcyA8LSBmaXRfdHJlbmRzICU+JSANCiAgZm9yZWNhc3QoaCA9IDEyKQ0KDQpmaXRfdHJlbmRzICU+JSANCiAgYXVnbWVudCgpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSkpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gc2FsZXNfMiksIGNvbG91ciA9ICJibGFjayIpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gLmZpdHRlZCwgY29sb3VyID0gLm1vZGVsKSwgc2l6ZSA9IDEpICsNCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIsIGRpcmVjdGlvbiA9IC0xKSArDQogIHNjYWxlX3lfY29udGludW91cygNCiAgICBsYWJlbHMgPSBzY2FsZXM6OmxhYmVsX251bWJlcihzdWZmaXggPSAiSyIsIHNjYWxlID0gMWUtMykNCiAgKSArDQogIGF1dG9sYXllcihmY190cmVuZHMsIGFscGhhID0gMC4zLCBsZXZlbCA9IDkwKSArDQogIGxhYnMoDQogICAgeCA9IE5VTEwsIHkgPSBOVUxMLA0KICAgIHRpdGxlID0gIk9yaWdpbmFsIHZhbHVlcyBhbmQgZml0dGVkIHRyZW5kIiwgDQogICAgc3VidGl0bGUgPSBjdXJyZW50X2JyYW5kDQogICkNCmBgYA0KDQpJbiB0aGUgcGxvdCBiZWxvdyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHJlYWwgYW5kIGFkanVzdGVkIHZhbHVlcyBieSB0aGUgbW9kZWwgaXMNCnNob3duLiBUaGUgc2xhc2hlZCBsaW5lIHJlcHJlc2VudHMgdGhlIHBlcmZlY3Rpb24sIGluIHN1Y2ggYSB3YXkgdGhhdCB0aGUgZnVydGhlciB0aGUNCnBvaW50cyBhcmUgZnJvbSB0aGUgbGluZSwgdGhlIHdvcnNlIHRoZSBtb2RlbCBpcy4gSW4gdGhlIGxvd2VyIGxlZnQgY29ybmVyLCB0aGUgdmFsdWVzIG9mDQp0aGUgYHBpZWNld2lzZWAgbW9kZWwgYXJlIG11Y2ggYmV0dGVyIGFkanVzdGVkIHRvIHRoZSByZWFsaXR5IHdoaWxlIHRob3NlIG9mIHRoZSBgbG1gIGFyZQ0KZmFydGhlciBhd2F5LiBJbiBnZW5lcmFsLCB0aGVyZSBpcyBhIGJpZ2dlciBkaXNwZXJzaW9uIGluIHRoZSBgbG1gIG1vZGVsIHRoYW4gaW4gdGhlDQpgcGllY2V3aXNlYC4NCg0KYGBge3IgZml0dGVkLXZzLXJlYWwtdmFsdWVzfQ0KZml0X3RyZW5kcyAlPiUgDQogIGF1Z21lbnQoKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHNhbGVzXzIsIHkgPSAuZml0dGVkKSkgKw0KICBnZW9tX3BvaW50KGFlcyhmaWxsID0gLm1vZGVsKSwgc2hhcGUgPSAyMSwgc2l6ZSA9IDIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKA0KICAgIGxhYmVscyA9IHNjYWxlczo6bGFiZWxfbnVtYmVyKHN1ZmZpeCA9ICJLIiwgc2NhbGUgPSAxZS0zKQ0KICApICsNCiAgc2NhbGVfeV9jb250aW51b3VzKA0KICAgIGxhYmVscyA9IHNjYWxlczo6bGFiZWxfbnVtYmVyKHN1ZmZpeCA9ICJLIiwgc2NhbGUgPSAxZS0zKQ0KICApICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIiwgZGlyZWN0aW9uID0gLTEpICsNCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBsaW5ldHlwZSA9IDIpICsNCiAgbGFicygNCiAgICB4ID0gIkRhdGEgKG9yaWdpbmFsIHNhbGVzKSIsIA0KICAgIHkgPSAiRml0dGVkIChwcmVkaWN0ZWQgdmFsdWVzKSIsDQogICAgdGl0bGUgPSAiT3JpZ2luYWwgdmFsdWVzIGFuZCBmaXR0ZWQgdHJlbmQiLCANCiAgICBzdWJ0aXRsZSA9IGN1cnJlbnRfYnJhbmQNCiAgKQ0KYGBgDQoNCmBgYHtyIHBpZWNld2lzZS1tb2RlbC1kZWZpbml0aW9ufQ0KZ2V0X21vZGVscyA8LSBmdW5jdGlvbihicmFuZCkgew0KICANCiAgIyBEZWZpbmUgc3RlcC13aXNlIG1vZGVscw0KICBtZGwgPC0gbnVsbF9tb2RlbCgpDQogIGlmIChicmFuZCA9PSAiQnJhbmQgR3JvdXAgMTciKSB7DQogICAgbWRsICA8LSBUU0xNKHNhbGVzXzIgfiB0cmVuZChrbm90cyA9IHRzaWJibGU6OnllYXJtb250aCgiMjAxNS0wMyIpKSArIHNlYXNvbigpKQ0KICB9IGVsc2UgaWYgKGJyYW5kID09ICJCcmFuZCBHcm91cCAyNCIpIHsNCiAgICBtZGwgIDwtIFRTTE0oc2FsZXNfMiB+IHRyZW5kKGtub3RzID0gdHNpYmJsZTo6eWVhcm1vbnRoKCIyMDE1LTEwIikpICsgc2Vhc29uKCkpDQogIH0gZWxzZSBpZiAoYnJhbmQgPT0gIkJyYW5kIEdyb3VwIDMwIikgew0KICAgIG1kbCAgPC0gVFNMTShzYWxlc18yIH4gdHJlbmQoDQogICAgICBrbm90cyA9IGModHNpYmJsZTo6eWVhcm1vbnRoKCIyMDEzLTA3IiksIHRzaWJibGU6OnllYXJtb250aCgiMjAxNi0wMSIpKQ0KICAgICAgKSArIHNlYXNvbigpICkNCiAgfSBlbHNlIGlmIChicmFuZCA9PSAiQnJhbmQgR3JvdXAgMzEiKSB7DQogICAgbWRsICA8LSBUU0xNKHNhbGVzXzIgfiB0cmVuZChrbm90cyA9IHRzaWJibGU6OnllYXJtb250aCgiMjAxNS0wMSIpKSArIHNlYXNvbigpKQ0KICB9IGVsc2UgaWYgKGJyYW5kID09ICJCcmFuZCBHcm91cCAzNiIpIHsNCiAgICBtZGwgIDwtIFRTTE0oc2FsZXNfMiB+IHRyZW5kKGtub3RzID0gdHNpYmJsZTo6eWVhcm1vbnRoKCIyMDE1LTAxIikpICsgc2Vhc29uKCkpDQogIH0gZWxzZSBpZiAoYnJhbmQgPT0gIkJyYW5kIEdyb3VwIDQxIikgew0KICAgIG1kbCAgPC0gVFNMTShzYWxlc18yIH4gdHJlbmQoa25vdHMgPSB0c2liYmxlOjp5ZWFybW9udGgoIjIwMTUtMTEiKSkgKyBzZWFzb24oKSkNCiAgfSBlbHNlIGlmIChicmFuZCA9PSAiQnJhbmQgR3JvdXAgNTEsIDczLCA5MCIpIHsNCiAgICBtZGwgIDwtIFRTTE0oc2FsZXNfMiB+IHRyZW5kKGtub3RzID0gdHNpYmJsZTo6eWVhcm1vbnRoKCIyMDE1LTA3IikpICsgc2Vhc29uKCkpDQogIH0gZWxzZSBpZiAoYnJhbmQgPT0gIkJyYW5kIEdyb3VwIDk2LCA5NyIpIHsNCiAgICBtZGwgIDwtIFRTTE0oc2FsZXNfMiB+IHRyZW5kKGtub3RzID0gdHNpYmJsZTo6eWVhcm1vbnRoKCIyMDE3LTAxIikpICsgc2Vhc29uKCkpDQogIH0gZWxzZSBpZiAoYnJhbmQgPT0gIk90aGVycyIpIHsNCiAgICBtZGwgIDwtIFRTTE0oc2FsZXNfMiB+IHRyZW5kKGtub3RzID0gdHNpYmJsZTo6eWVhcm1vbnRoKCIyMDE2LTAxIikpICsgc2Vhc29uKCkpDQogIH0NCiAgDQogICMgUHJlcGFyZSBhIGxpc3Qgb2YgbW9kZWxzDQogIG1kbF9saXN0IDwtIGxpc3QoDQogICAgbG0gPSBUU0xNKHNhbGVzXzIgfiB0cmVuZCgpICsgc2Vhc29uKCkpLA0KICAgIHN0ZXB3aXNlID0gbWRsDQogICkNCiAgDQogICMgUmV0dXJuIGxpc3QNCiAgcmV0dXJuKG1kbF9saXN0KQ0KfQ0KYGBgDQoNClRoZSBtb2RlbHMgYXJlIGRlZmluZWQgbWFudWFsbHkgZm9yIGVhY2ggYnJhbmQuDQoNCmBgYHtyIHBpZWNld2lzZS1tb2RlbC1kZWZpbml0aW9uLWV4YW1wbGUsIGVjaG89VFJVRSwgZXZhbD1GQUxTRX0NCmdldF9tb2RlbHNfIDwtIGZ1bmN0aW9uKGJyYW5kKSB7DQogICMgRGVmaW5lIHN0ZXAtd2lzZSBtb2RlbHMNCiAgbWRsIDwtIG51bGxfbW9kZWwoKQ0KICBpZiAoYnJhbmQgPT0gIkJyYW5kIEdyb3VwIDE3Iikgew0KICAgIG1kbCAgPC0gVFNMTShzYWxlc18yIH4gdHJlbmQoa25vdHMgPSB0c2liYmxlOjp5ZWFybW9udGgoIjIwMTUtMDMiKSkgKyBzZWFzb24oKSkNCiAgfSBlbHNlIGlmIChicmFuZCA9PSAiQnJhbmQgR3JvdXAgMjQiKSB7DQogICAgLi4uDQogIH0NCn0NCmBgYA0KDQpJbiBicmFuZHMgc3VjaCBhcyAqQnJhbmQgR3JvdXAgMTcqIG9yICpCcmFuZCBHcm91cCAyNCogdGhlIGVmZmVjdCBpcyByZW1hcmthYmxlLiBJdCBpcw0KYWxzbyBpbiBvdGhlcnMsIHN1Y2ggYXMgdGhlICpCcmFuZCBHcm91cCAzMSogYXMgd2UgaGF2ZSBqdXN0IHNlZW4gaW4gdGhlIHByZXZpb3VzIHBsb3RzLg0KDQpgYGB7ciBmaXQtc3RlcHdpc2UtYnktYnJhbmR9DQojIEZpdCBtb2RlbHMgZm9yIGFsbCBicmFuZA0KYnJhbmRzIDwtIHVuaXF1ZSh0cmFpbl9kYXRhJGJyYW5kKSAjIGFsbF9sZXZlbHNfdGJsDQpmaXRfc3RlcHdpc2VfYnJhbmRzIDwtIG1hcF9kZigNCiAgLnggPSBicmFuZHMsIA0KICAuZiA9IH4gYnlfYnJhbmRfdHNpYmJsZSAlPiUgDQogICAgZmlsdGVyKGJyYW5kID09IC54KSAlPiUgDQogICAgbW9kZWwoISEhZ2V0X21vZGVscygueCkgKQ0KKQ0KYGBgDQoNCmBgYHtyIHZpei1zdGVwd2lzZS1icmFuZHMsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTl9DQpwMSA8LSBmaXRfc3RlcHdpc2VfYnJhbmRzICU+JSANCiAgZmlsdGVyKGJyYW5kICVpbiUgYygiQnJhbmQgR3JvdXAgMTciLCAiQnJhbmQgR3JvdXAgMjQiKSkgJT4lIA0KICBzZWxlY3QoYnJhbmQsIGxtLCBzdGVwd2lzZSkgJT4lIA0KICBmb3JlY2FzdChuZXdfZGF0YSA9IGJ5X2JyYW5kX3RzaWJibGUpICU+JSANCiAgYXV0b3Bsb3QoYnlfYnJhbmRfdHNpYmJsZSwgbGV2ZWwgPSBOVUxMLCBhbHBoYSA9IDAuOSwgc2l6ZSA9IDEpICsNCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIsIGRpcmVjdGlvbiA9IC0xKSArDQogIHNjYWxlX3lfY29udGludW91cygNCiAgICBsYWJlbHMgPSBzY2FsZXM6OmxhYmVsX251bWJlcihzdWZmaXggPSAiSyIsIHNjYWxlID0gMWUtMykNCiAgKSArIA0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDIiLCBkaXJlY3Rpb24gPSAtMSkgKw0KICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCkNCg0KcDIgPC0gZml0X3N0ZXB3aXNlX2JyYW5kcyAlPiUgDQogIGZpbHRlcihicmFuZCAlaW4lIGMoIkJyYW5kIEdyb3VwIDMwIiwgIkJyYW5kIEdyb3VwIDMxIikpICU+JSANCiAgc2VsZWN0KGJyYW5kLCBsbSwgc3RlcHdpc2UpICU+JSANCiAgZm9yZWNhc3QobmV3X2RhdGEgPSBieV9icmFuZF90c2liYmxlKSAlPiUgDQogIGF1dG9wbG90KGJ5X2JyYW5kX3RzaWJibGUsIGxldmVsID0gTlVMTCwgYWxwaGEgPSAwLjksIHNpemUgPSAxKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDIiLCBkaXJlY3Rpb24gPSAtMSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgbGFiZWxzID0gc2NhbGVzOjpsYWJlbF9udW1iZXIoc3VmZml4ID0gIksiLCBzY2FsZSA9IDFlLTMpDQogICkgKyANCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIiwgZGlyZWN0aW9uID0gLTEpICsNCiAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwpDQoNCihwMSB8IHAyKSArIHBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikNCmBgYA0KDQojIyMgQWxsIHNlcmllcyBhbmFseXNpcw0KDQpOb3cgdGhhdCB3ZSBoYXZlIGlkZW50aWZpZWQgdGhlICprbm90cyogb2YgZWFjaCBicmFuZCBpcyB0aW1lIHRvIGFwcGx5IHRoaXMgbW9kZWxzIGludG8NCnRoZSBwcm9kdWN0IGxldmVsIHNlcmllcy4NCg0KYGBge3IgZml0LXN0ZXB3aXNlLW9uLXNrdS1sZXZlbCwgZWNobz1UUlVFfQ0KZml0X3N0ZXB3aXNlIDwtIG1hcF9kZnIoDQogIC54ID0gc2V0TmFtZXMoYnJhbmRzLCBicmFuZHMpLCANCiAgLmYgPSB+IHRyYWluX2RhdGEgJT4lIA0KICAgIGZpbHRlcihicmFuZCA9PSAueCkgJT4lIA0KICAgIG1vZGVsKCEhIWdldF9tb2RlbHMoLngpWyJzdGVwd2lzZSJdKSwgDQogIC5pZCA9ICJicmFuZCINCikNCmBgYA0KDQpPdmVyYWxsIGFjY3VyYWN5IGlzIG5vdCBhcyBnb29kIGFzIGBjb21ibmAsIGBhcmltYWAgb3JgZXRzYCBtb2RlbHMgdHJhaW5lZCBvbiBbc3RhdGlzdGljYWwNCmJlbmNobWFya3Mgc2VjdGlvbl0oI3N0YXRpc3RpY2FsLWJlbmNobWFya3MpDQoNCmBgYHtyIGZjLWFuZC1hY2Mtc3RlcHdpc2V9DQojIEdlbmVyYXRlIGZvcmVjYXN0cyBmb3IgdGhlIHZhbGlkYXRpb24gcGVyaW9kDQpmY19zdGVwd2lzZSA8LSBmaXRfc3RlcHdpc2UgJT4lIA0KICBmb3JlY2FzdChuZXdfZGF0YSA9IHZhbGlkX2RhdGEpDQoNCiMgQ2FsY3VsYXRlIGFjY3VyYWN5IGZvciBlYWNoIHNlcmllcyBhbmQgbW9kZWwNCmFjY19zdGVwd2lzZSA8LSBmY19zdGVwd2lzZSAlPiUgDQogIGFjY3VyYWN5KGJpbmRfcm93cyh0cmFpbl9kYXRhLCB2YWxpZF9kYXRhKSwgbWVhc3VyZXMpICU+JSANCiAgYWRkX3JhbmsodmFyID0gIk1BUEUiKQ0KDQojIEFnZ3JlZ2F0ZSBtZXRyaWNzIGJ5IGAubW9kZWxgDQphY2Nfc3RlcHdpc2UgJT4lIA0KICBncm91cF9ieSgubW9kZWwpICU+JSANCiAgc3VtbWFyaXNlX2F0KHZhcnMoUk1TU0U6Q1JQUyksIG1lYW4pICU+JSANCiAgYXJyYW5nZShNQVBFKSAlPiUgDQogIHRvX2h0bWwoZGlnaXRzID0gMSkNCmBgYA0KDQpMZXQncyBsb29rIGF0IHNvbWUgb2YgdGhlIHdvcnN0IGZvcmVjYXN0LiBUaGUgYGFyaW1hYCBhbmQgYGV0c2AgbW9kZWxzIHdpbGwgYmUgaW5jbHVkZWQgYXMNCmEgcmVmZXJlbmNlLiBJbiB0aGF0IHdheSwgd2UgY2FuIHVuZGVyc3RhbmQgaG93IHRoZSBgcGllY2V3aXNlYCBtb2RlbCBpcyBtb2RlbGluZyB0aGUNCmRhdGEuIEluIHRoZSBmb2xsb3dpbmcgY2FzZSwgaXQgc2VlbXMgdGhhdCB0aGUgbW9kZWwgaGFzIGZhaWxlZCB0byBjYXB0dXJlIHRoZSB0cmVuZA0KY29ycmVjdGx5IGFuZCBpcyB1bmRlci1mb3JlY2FzdGluZyBtdWNoIG1vcmUgdGhhbiB0aGUgb3RoZXIgdHdvIG1vZGVscy4NCg0KYGBge3IgcGllY2V3aXNlLXdvcnN0LWZvcmVjYXN0cywgZXZhbD1GQUxTRX0NCiMgIyBJbnNwZWNjaW9uYW1vcyBsYXMgcGVvcmVzIHNlcmllcw0KIyBhY2Nfc3RlcHdpc2UgJT4lIA0KIyAgIGFycmFuZ2UoZGVzYyhNQVBFKSkgJT4lIA0KIyAgIGhlYWQoKQ0KYGBgDQoNCmBgYHtyIHZpei1waWVjZXdpc2Utd29yc3QtZm9yZWNhc3RzfQ0KZmNfc3RlcHdpc2UgJT4lDQogIGJpbmRfcm93cyhmY19zdGF0aXN0aWNhbCkgJT4lIA0KICBmaWx0ZXIoDQogICAgaWQgJWluJSBjKCJDbHVzdGVyIDlfX0JyYW5kIEdyb3VwIDMwIiksIA0KICAgIC5tb2RlbCAlaW4lIGMoImFyaW1hIiwgImV0cyIsICJzdGVwd2lzZSIpDQogICkgJT4lIA0KICBhdXRvcGxvdChiaW5kX3Jvd3ModHJhaW5fZGF0YSwgdmFsaWRfZGF0YSksIGxldmVsID0gTlVMTCwgYWxwaGEgPSAwLjkpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGlkKSkgKw0KICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCkNCmBgYA0KDQpXaGF0IHdlIGNhbiBkbyBub3c/IExldCdzIHBsb3QgdGhlIHRyZW5kIHRvIHVuZGVyc3RhbmQgd2h5IHRoaXMgaXMgaGFwcGVuaW5nLiBUaGUgbW9kZWwNCmZpdHMgdmVyeSB3ZWxsIHRoZSB0cmVuZCB1cCB0byAyMDE2IGJ1dCwgYWZ0ZXIgdGhhdCBwZXJpb2QsIHRoZSB0cmVuZCBpcyBzdGlsbCBpbiBhDQpkb3dudHJlbmQgYW5kIGl0IHNob3VsZCByZW1haW4gYWxtb3N0IGZsYXQuIFRoZSB2ZXJ0aWNhbCBkYXNoZWQgbGluZXMgaW5kaWNhdGUgdGhlICprbm90cyoNCmRlZmluZWQgd2l0aGluIHRoZSBtb2RlbC4NCg0KYGBge3J9DQojIEZpdCB0cmVuZCBtb2RlbCBiZWNhdXNlIHdlIHdhbnQgdG8gc2VlIGhvdyBpcyB0cmVuZCBjYXB0dXJlZA0KZml0X3RyZW5kXzMwIDwtIHRyYWluX2RhdGEgJT4lIA0KICBmaWx0ZXIoaWQgJWluJSBjKCJDbHVzdGVyIDlfX0JyYW5kIEdyb3VwIDMwIikpICU+JSANCiAgbW9kZWwoDQogICAgcGllY2V3aXNlID0gVFNMTShzYWxlc18yIH4gdHJlbmQoDQogICAgICBrbm90cyA9IGModHNpYmJsZTo6eWVhcm1vbnRoKCIyMDEzLTA3IiksIA0KICAgICAgICAgICAgICAgIHRzaWJibGU6OnllYXJtb250aCgiMjAxNi0wMSIpKSkpDQogICkNCg0KIyBGb3JlY2FzdCB0cmVuZCBtb2RlbA0KZmNfdHJlbmRfMzAgPC0gZml0X3RyZW5kXzMwICU+JSANCiAgZm9yZWNhc3QoaCA9IDEyKQ0KDQojIERpc3BsYXkgcmVhbCB2cyBmaXR0ZWQgZGF0YSBhbmQgZm9yZWNhc3RlZCB0cmVuZA0KZml0X3RyZW5kXzMwICU+JSANCiAgYXVnbWVudCgpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSkpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gc2FsZXNfMiksIGNvbG91ciA9ICJibGFjayIsIHNlcmllcyA9ICJEYXRhIikgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSAuZml0dGVkKSwgY29sb3VyID0gInN0ZWVsYmx1ZSIsIHNlcmllcyA9ICIuZml0dGVkIikgKw0KICBhdXRvbGF5ZXIoZmNfdHJlbmRfMzAsIGNvbG91ciA9ICJzdGVlbGJsdWUiLCBhbHBoYSA9IDAuMywgbGV2ZWwgPSA5MCkgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKGFzLkRhdGUoIjIwMTMtMDctMDEiKSwgYXMuRGF0ZSgiMjAxNi0wMS0wMSIpKSwgDQogICAgICAgICAgICAgbGluZXR5cGUgPSAyLCBzaXplID0gMSwgY29sb3IgPSAiZ3JleSIsIGFscGhhID0gMC40KSArDQogIGxhYnMoDQogICAgeCA9IE5VTEwsDQogICAgeSA9ICJEYXRhIHZzIEZpdHRlZCAocHJlZGljdGVkIHZhbHVlcykiLA0KICAgIHRpdGxlID0gIlRyZW5kIGNhcHR1cmVkIGJ5IHRoZSBwaWVjZXdpc2UgbW9kZWwiLCANCiAgICBzdWJ0aXRsZSA9ICJDbHVzdGVyIDlfX0JyYW5kIEdyb3VwIDMwIg0KICApDQpgYGANCg0KRmluYWxseSwgd2Ugc2hvdyB0aGUgZm9yZWNhc3QgZm9yIHRoZSBzZXJpZXMgdGhhdCB3ZSBoYXZlIGJlZW4gbG9va2luZyBpbiB0aGUgcHJldmlvdXMNCnNlY3Rpb25zLg0KDQpgYGB7ciB2aXotc3RlcHdpc2UtMDJ9DQpmaXRfc3RlcHdpc2UgJT4lDQogIGZpbHRlcihpZCA9PSAiQ2x1c3RlciA3X19CcmFuZCBHcm91cCAzMCIpICU+JQ0KICBsZWZ0X2pvaW4oc2VsZWN0KGZpdF9zdGF0aXN0aWNhbCwgaWQsIGV0cywgYXJpbWEpLCBieSA9ICJpZCIpICU+JSANCiAgZm9yZWNhc3QobmV3X2RhdGEgPSB2YWxpZF9kYXRhKSAlPiUgDQogIHBsb3RfZm9yZWNhc3QoZmFjZXRzID0gIi5tb2RlbCIsIG5jb2wgPSBOVUxMLCBsZXZlbCA9IE5VTEwsIHRpdGxlID0gIiIpICsNCiAgZmFjZXRfd3JhcCh+IGlkKQ0KYGBgDQoNCmBgYHtyIHZpei1zdGVwd2lzZS0wMSwgZWNobz1UUlVFfQ0KZml0X3N0ZXB3aXNlICU+JSANCiAgZmlsdGVyKGlkID09ICJDbHVzdGVyIDJfX090aGVycyIpICU+JQ0KICBsZWZ0X2pvaW4oc2VsZWN0KGZpdF9zdGF0aXN0aWNhbCwgaWQsIGV0cywgYXJpbWEpLCBieSA9ICJpZCIpICU+JSANCiAgZm9yZWNhc3QobmV3X2RhdGEgPSB2YWxpZF9kYXRhKSAlPiUgDQogIHBsb3RfZm9yZWNhc3QoZmFjZXRzID0gIi5tb2RlbCIsIG5jb2wgPSBOVUxMLCBsZXZlbCA9IE5VTEwsIHRpdGxlID0gIiIpICsNCiAgZmFjZXRfd3JhcCh+IGlkKQ0KYGBgDQoNCiMjIFNlbGVjdGluZyBiZXN0IG1vZGVsDQoNClRocm91Z2hvdXQgdGhlIG5vdGVib29rIHdlIGhhdmUgc2VlbiBob3cgZWFjaCBtb2RlbCBpcyBiZXR0ZXIgc3VpdGVkIHRvIG9uZSBzcGVjaWZpYyBjYXNlLA0KaW4gc29tZSBjYXNlcyBhIG1vZGVsIGFzIHNpbXBsZSBhcyB0aGUgYG5haXZlYCBnaXZlIHVzIHRoZSBiZXN0IHJlc3VsdHMsIGhvd2V2ZXIsIGluIG90aGVyDQpjYXNlcyB3ZSBuZWVkIG1vcmUgY29tcGxleCBtb2RlbHMgdG8gY2FwdHVyZSB0aGUgc2lnbmFsIG9mIHRoZSBzZXJpZXMuIFNvLCB3aHkgbm90IHNlbGVjdA0KdGhlIGJlc3QgbW9kZWwgZm9yIGVhY2ggc2VyaWVzIGluc3RlYWQgb2YgYWx3YXlzIHVzaW5nIHRoZSBzYW1lIG1vZGVsIGZvciBldmVyeXRoaW5nPw0KDQpgYGB7ciBhY2N1cmFjeS1hbGwtbW9kZWxzLCBlY2hvPVRSVUV9DQojIENvbWJpbmUgYWxsIGFjY3VyYWNpZXMgdG9nZXRoZXINCmFjY19hbGwgPC0gDQogIGJpbmRfcm93cyhhY2NfYmFzZWxpbmVzLCBhY2Nfc3RhdGlzdGljYWwsIGFjY194cmVnLCBhY2Nfc3RlcHdpc2UpICU+JSANCiAgYWRkX3JhbmsodmFyID0gIk1BUEUiKQ0KDQojIFNlbGVjdCBiZXN0IG1vZGVsIGZvciBlYWNoIHNlcmllDQphY2NfYmVzdF9tb2RlbHMgPC0gYWNjX2FsbCAlPiUgDQogIGFycmFuZ2UoaWQsIHJhbmspICU+JSANCiAgdG9wX24oLXJhbmssIG4gPSAxKSAlPiUgDQogIGRwbHlyOjpncm91cF9ieShpZCkgJT4lIA0KICBkcGx5cjo6c2xpY2UoMSkgJT4lIA0KICB1bmdyb3VwKCkNCmBgYA0KDQpUaGUgZXJyb3IgaGFzIGJlZW4gcmVkdWNlZCBieSBhbiA0NyUgd2l0aCByZXNwZWN0IHRvIHRoZSBgbmFpdmVgIGFuZCAyOCUgd2l0aCByZXNwZWN0IHRvDQp0aGUgYGNvbWJuYCBtb2RlbC4NCg0KYGBge3IgZXZhbD1GQUxTRX0NCiMgbm4gPC0gMjYuOSAgICAjIG5haXZlIG1vZGVsDQojIGNjIDwtIDE0LjEgICAgIyBiZXN0IG1vZGVsIGZvciBlYWNoIHNlcmllDQojIChjYyAvIG5uKSAtIDEgIyByZWR1Y3Rpb24gcGVyY2VudGFnZQ0KIyAjIFsxXSAwLjQ3NTgzNjQNCiMgDQojIG5uIDwtIDE5LjggICAgIyBjb21ibiBtb2RlbA0KIyBjYyA8LSAxNC4xICAgICMgYmVzdCBtb2RlbCBmb3IgZWFjaCBzZXJpZQ0KIyAoY2MgLyBubikgLSAxICMgcmVkdWN0aW9uIHBlcmNlbnRhZ2UNCiMgIyAtMC4yODc4Nzg4DQpgYGANCg0KYGBge3IgYWNjdXJhY3ktc3VtbWFyeS1iZXN0LW1vZGVsLCBlY2hvPVRSVUV9DQphY2NfYmVzdF9tb2RlbHMgJT4lIA0KICBzdW1tYXJpc2VfYXQodmFycyhSTVNTRTpDUlBTKSwgbWVhbikgJT4lIA0KICBhZGRfY29sdW1uKC5tb2RlbCA9ICJCZXN0IG1vZGVsIiwgLmJlZm9yZSA9IDEpICU+JSANCiAgYXJyYW5nZShNQVBFKSAlPiUgDQogIHRvX2h0bWwoZGlnaXRzID0gMSkNCmBgYA0KDQpgYGB7ciBzYWZldHktY2hlY2stbW9kZWwtdGllc30NCiMgIyBTYWZldHkgY2hlY2s6IGRvIG5vdCBleGlzdCBkdXBsaWNhdGVzIGluIGNhc2Ugb2YgdGllcy4NCiMgYmVzdF9tb2RlbHMgJT4lIA0KIyAgIGNvdW50KGlkLCBzb3J0ID0gVCkNCmBgYA0KDQpIb3cgbWFueSB0aW1lcyBlYWNoIG1vZGVsIGhhcyBiZWVuIHNlbGVjdGVkPyBgc3RlcHdpc2VgIGFuZCBgYXJpbWFgIGFyZSB0aGUgdHdvIGJlc3QgbW9kZWxzDQpmb2xsb3dlZCBieSBgeHJlZ180YC4gUmVjYWxsIHRoYXQgdGhpcyBtb2RlbCB1c2VzIGFsbCB0aGUgaW52ZXN0bWVudHMgcHJvdmlkZWQuDQoNCmBgYHtyIHRpbWUtZWFjaC1tb2RlbC1zZWxlY3RlZCwgZmlnLmhlaWdodD01fQ0KYWNjX2Jlc3RfbW9kZWxzICU+JSANCiAgY291bnQoLm1vZGVsLCBzb3J0ID0gVFJVRSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBmY3RfcmVvcmRlcigubW9kZWwsIG4pLCB5ID0gbikpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICIjNzI5RUNFIiwgYWxwaGEgPSAwLjkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKG4uYnJlYWtzID0gMTApICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJIb3cgbWFueSB0aW1lcyBlYWNoIG1vZGVsIGhhcyBiZWVuIHNlbGVjdGVkPyIsIA0KICAgIHggPSAiIiwgeSA9ICJDb3VudCINCiAgKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwgDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKQ0KICApDQpgYGANCg0KSW4gdGhlIHNhbWUgd2F5IHRoYXQgd2UgaGF2ZSBiZWVuIGRvaW5nLCB3ZSBhcmUgZ29pbmcgdG8gc2hvdyB0aGUgYmVzdCBhbmQgd29yc3QNCnByZWRpY3Rpb25zIHRvIGdldCBhbiBpZGVhIG9mIHdoYXQgdGhlIHByZWRpY3Rpb25zIGFyZSBsaWtlLg0KDQpgYGB7ciBmb3JlY2FzdC1hbGwtbW9kZWxzLCBlY2hvPVRSVUV9DQojIENvbWJpbmUgYWxsIGZvcmVjYXN0cw0KZmNfYWxsX21vZGVscyA8LSBiaW5kX3Jvd3MoZmNfYmFzZWxpbmVzLCBmY19zdGF0aXN0aWNhbCwgZmNfeHJlZywgZmNfc3RlcHdpc2UpDQoNCmZjX2Jlc3RfbW9kZWxzIDwtIGZjX2FsbF9tb2RlbHMgJT4lIA0KICBzZW1pX2pvaW4oYWNjX2Jlc3RfbW9kZWxzLCBieSA9IGMoIi5tb2RlbCIsICJpZCIpKQ0KYGBgDQoNCmBgYHtyIGJlc3QtbW9kZWxzLWJlc3QtZm9yZWNhc3RzLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD05fQ0KdG9wX25fZm9yZWNhc3RzIDwtIGFjY19iZXN0X21vZGVscyAlPiUgDQogIGFycmFuZ2UoTUFQRSkgJT4lIA0KICBoZWFkKDQpDQoNCmZjX2Jlc3RfbW9kZWxzICU+JSANCiAgZmlsdGVyKGlkICVpbiUgdG9wX25fZm9yZWNhc3RzJGlkKSAlPiUgDQogIHBsb3RfZm9yZWNhc3QoZmFjZXRzID0gImlkIiwgbGV2ZWwgPSBOVUxMLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWVfeSIsIA0KICAgICAgICAgICAgICAgIHRpdGxlID0gIkJlc3QgcGVyZm9ybWluZyBmb3JlY2FzdCIpDQogICMgbGFicyh0aXRsZSA9ICJCZXN0IHBlcmZvcm1pbmcgZm9yZWNhc3QiKQ0KYGBgDQoNClNvbWV0aW1lcywgYW55IG1vZGVsIGlzIGFibGUgdG8gcHJlZGljdCB0aGUgZGF0YSBhY2N1cmF0ZWx5LiBNaWdodCBiZSwgYmVjYXVzZSB0aGUgc2VyaWVzDQphcmUgd2hpdGUgbm9pc2Ugb3IgdGhlIHNpZ25hbC10by1ub2lzZSByYXRpbyBpcyB2ZXJ5IGxvdy4gT3RoZXIgdGltZXMsIHRoZXJlIGFyZSB2ZXJ5DQphYnJ1cHQgY2hhbmdlcyB0aGF0IGNhbm5vdCBiZSBleHBsYWluZWQgYnkgYW55IHByZWRpY3RvciAocGFzdCBzYWxlcywgaW52ZXN0bWVudHMsIGV0Yy4pDQoNCmBgYHtyIGJlc3QtbW9kZWxzLXdvcnN0LWZvcmVjYXN0cywgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9OX0NCndvcnN0XzRfZm9yZWNhc3RzIDwtIGFjY19iZXN0X21vZGVscyAlPiUgDQogIGFycmFuZ2UoTUFQRSkgJT4lIA0KICB0YWlsKDQpDQoNCmZjX2Jlc3RfbW9kZWxzICU+JSANCiAgZmlsdGVyKGlkICVpbiUgd29yc3RfNF9mb3JlY2FzdHMkaWQpICU+JSANCiAgcGxvdF9mb3JlY2FzdChmYWNldHMgPSAiaWQiLCBsZXZlbCA9IE5VTEwsIG5jb2wgPSAyLCBzY2FsZXMgPSAiZnJlZV95IiwgDQogICAgICAgICAgICAgICAgdGl0bGUgPSAiV29yc3QgcGVyZm9ybWluZyBmb3JlY2FzdCIpDQogICMgbGFicyh0aXRsZSA9ICJXb3JzdCBwZXJmb3JtaW5nIGZvcmVjYXN0IikNCmBgYA0KDQojIyMgRHJpbGwgZG93biBhY2N1cmFjeQ0KDQpGb3JlY2FzdHMgYXJlIG9mdGVuIHJlcXVpcmVkIGZvciBhbGwgbGV2ZWxzIG9mIHRoZSBzZXJpZXMsIGFuZCBpdCBpcyBuYXR1cmFsIHRoYXQgdGhlDQpmb3JlY2FzdHMgdG8gYWRkIHVwIGluIHRoZSBzYW1lIHdheSBhcyB0aGUgZGF0YS4gVGhpcyBpcyBpbXBvcnRhbnQgYmVjYXVzZSB0aGUgZm9yZWNhc3QNCndpbGwgYmUgYW5hbHl6ZWQgZnJvbSBkaWZmZXJlbnQgcG9pbnRzIG9mIHZpZXcgYW5kIGFsbCBvZiB0aGVtIG11c3QgYmUgYWxpZ25lZC4gRm9yDQpleGFtcGxlLCBhIGJyYW5kIG1hbmFnZXIgbWlnaHQgYmUgaW50ZXJlc3RlZCBpbiB0aGUgZm9yZWNhc3Qgb2YgYWxsIG9mIHRoZWlyIHByb2R1Y3RzIGJ1dA0KaXQgYWxzbyB3b3VsZCBtaWdodCBnZXQgYSB2aWV3IGF0IGNvdW50cnkgbGV2ZWwgb2YgdGhlaXIgYnJhbmQuIEF0IHRoZSBzYW1lIHRpbWUsIHRoZSBDRU8NCm9mIHRoZSBjb21wYW55LCBtaWdodCBiZSBpbnRlcmVzdGVkIGluIGhpZ2hlciBsZXZlbHMgb2YgYWdncmVnYXRpb24uDQoNClRoZSBlYXNpZXN0IGJyYW5kIHRvIGZvcmVjYXN0IGlzICpCcmFuZCBHcm91cCA5NiwgOTcqIGFuZCB0aGUgaGFyZGVzdCBpcyAqQnJhbmQgR3JvdXAgMjQqDQp3aGljaCBpcyBub3QgYSBzdXJwcmlzZSBnaXZlbiBpdCdzIGhpZ2hlc3QgZ3Jvd3RoLg0KDQpgYGB7ciBhY2N1cmFjeS1zdW1tYXJ5LWJlc3QtbW9kZWwtYnJhbmQtbGV2ZWx9DQphY2NfYmVzdF9tb2RlbHMgJT4lIA0KICBzZXBhcmF0ZShjb2wgPSAiaWQiLCBpbnRvID0gYygiY2x1c3RlciIsICJicmFuZCIpLCBzZXAgPSAiX18iKSAlPiUgDQogIGdyb3VwX2J5KGJyYW5kKSAlPiUgDQogIHN1bW1hcmlzZV9hdCh2YXJzKFJNU1NFOkNSUFMpLCBtZWRpYW4pICU+JSANCiAgYWRkX2NvbHVtbigubW9kZWwgPSAiQmVzdCBtb2RlbCIsIC5iZWZvcmUgPSAxKSAlPiUgDQogIGFycmFuZ2UoTUFQRSkgJT4lIA0KICB0b19odG1sKGRpZ2l0cyA9IDEsIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQpJbiB0aGUgc2FtZSB3YXksIHRoZSBlYXNpZXN0IGNsdXN0ZXIgaXMgKkNsdXN0ZXIgOCogYW5kIHRoZSBoYXJkZXN0IGlzICpDbHVzdGVyIDcqDQoNCmBgYHtyIGFjY3VyYWN5LXN1bW1hcnktYmVzdC1tb2RlbC1jbHVzdGVyLWxldmVsfQ0KYWNjX2Jlc3RfbW9kZWxzICU+JSANCiAgc2VwYXJhdGUoY29sID0gImlkIiwgaW50byA9IGMoImNsdXN0ZXIiLCAiYnJhbmQiKSwgc2VwID0gIl9fIikgJT4lIA0KICBncm91cF9ieShjbHVzdGVyKSAlPiUgDQogIHN1bW1hcmlzZV9hdCh2YXJzKFJNU1NFOkNSUFMpLCBtZWRpYW4pICU+JSANCiAgYWRkX2NvbHVtbigubW9kZWwgPSAiQmVzdCBtb2RlbCIsIC5iZWZvcmUgPSAxKSAlPiUgDQogIGFycmFuZ2UoTUFQRSkgJT4lIA0KICB0b19odG1sKGRpZ2l0cyA9IDEsIGZ1bGxfd2lkdGggPSBGQUxTRSkNCmBgYA0KDQojIyBNYWtlIGZvcmVjYXN0cw0KDQpUaGUgbGFzdCBzdGVwIGlzIGdlbmVyYXRlIHRoZSBmb3JlY2FzdCBmb3IgYWxsIHRoZSA3NSBzZXJpZXMuIE5vdywgd2UgbXVzdCBlc3RpbWF0ZSB0aGUNCm1vZGVscyBhZ2FpbiBidXQgdXNpbmcgYWxsIHRoZSBkYXRhIGF2YWlsYWJsZS4NCg0KYGBge3Igc3RhcnQtcGFyYWxsZWxpemF0aW9uLWFnYWlufQ0KIyAjIFN0YXJ0IHBhcmFsbGVsaXphdGlvbiBhZ2Fpbg0KIyBteV9wbGFuIDwtIGZ1dHVyZTo6cGxhbigibXVsdGlzZXNzaW9uIiwgd29ya2VycyA9IDJMKQ0KYGBgDQoNCmBgYHtyIHJlZml0LW1vZGVscywgZWNobz1UUlVFLCBldmFsPUZBTFNFfQ0KIyBDb21iaW5lIGFsbCB0aHJlZSBsaXN0IG9mIG1vZGVscw0KbW9kZWxzX2xpc3QgPC0gYyhtb2RlbHMsIG1vZGVsc19zdGF0LCBtb2RlbHNfeHJlZykNCg0KIyBGaXQgbW9kZWxzIHVzaW5nIGFsbCB0cmFpbiBkYXRhDQpmaXRfYWxsX21vZGVsc18xIDwtIHRyYWluX2RhdGEgJT4lDQogIGJpbmRfcm93cyh2YWxpZF9kYXRhKSAlPiUNCiAgbW9kZWwoISEhbW9kZWxzX2xpc3QpICU+JQ0KICBtdXRhdGUoY29tYm4gPSAoYXJpbWEgKyBldHMpIC8gMikNCg0KIyBUcmFpbiBwaWVjZS13aXNlIG1vZGVscw0KZml0X2FsbF9tb2RlbHNfMiA8LSBtYXBfZGZyKA0KICAueCA9IHNldE5hbWVzKGJyYW5kcywgYnJhbmRzKSwNCiAgLmYgPSB+IHRyYWluX2RhdGEgJT4lDQogICAgYmluZF9yb3dzKHZhbGlkX2RhdGEpICU+JQ0KICAgIGZpbHRlcihicmFuZCA9PSAueCkgJT4lDQogICAgbW9kZWwoISEhZ2V0X21vZGVscygueClbInN0ZXB3aXNlIl0pLA0KICAuaWQgPSAiYnJhbmQiDQopDQoNCiMgQ29tYmluZSBhbGwgbW9kZWxzIHRvZ2V0aGVyDQpmaXRfYWxsX21vZGVscyA8LSBmaXRfYWxsX21vZGVsc18xICU+JSANCiAgbGVmdF9qb2luKGZpdF9hbGxfbW9kZWxzXzIsIGJ5ID0gImlkIikNCg0KIyBTYXZlIG1vZGVscw0Kd3JpdGVfcmRzKGZpdF9hbGxfbW9kZWxzLCANCiAgZmlsZS5wYXRoKHBhdGhfbW9kZWxzLCAiZml0X2FsbF9tb2RlbHMucmRzIiksIA0KICBjb21wcmVzcyA9ICJneiIpDQpgYGANCg0KYGBge3IgbG9hZC1tb2RlbHN9DQojIExvYWQgYWxsIG1vZGVscw0KZml0X2FsbF9tb2RlbHMgPC0gcmVhZF9yZHMoZmlsZS5wYXRoKHBhdGhfbW9kZWxzLCAiZml0X2FsbF9tb2RlbHMucmRzIikpDQoNCmdldF9iZXN0X21vZGVsX2NvbHVtbiA8LSBmdW5jdGlvbihkYXRhLCAuLi4pIHsNCiAgdHJhbnNtdXRlKGRhdGEsIC4uLiwgYmVzdF9tb2RlbCA9IGdldChkYXRhJC5tb2RlbCkpDQp9DQoNCiMgS2VlcCBvbmx5IHRoZSBiZXN0IG1vZGVsIGZvciBlYWNoIGBpZGANCmZpdF9iZXN0X21vZGVscyA8LSBmaXRfYWxsX21vZGVscyAlPiUgDQogIGxlZnRfam9pbihzZWxlY3QoYWNjX2Jlc3RfbW9kZWxzLCBpZCwgLm1vZGVsKSwgYnkgPSAiaWQiKSAlPiUgDQogIHNwbGl0KC4kaWQpICU+JQ0KICBtYXBfZGYoZ2V0X2Jlc3RfbW9kZWxfY29sdW1uLCBpZCwgLm1vZGVsKSAlPiUNCiAgYXNfbWFibGUoa2V5ID0gImlkIiwgbW9kZWwgPSAiYmVzdF9tb2RlbCIpDQpgYGANCg0KYGBge3IgZ2VuZXJhdGUtZm9yZWNhc3RzLWZvci0yMDE4fQ0KIyBHZW5lcmF0ZSBmb3JlY2FzdHMgZm9yIHRoZSBmdXR1cmUgKDIwMTgpDQpmY19mdXR1cmUgPC0gZml0X2Jlc3RfbW9kZWxzICU+JSANCiAgc2VsZWN0KC0ubW9kZWwpICU+JSANCiAgZm9yZWNhc3QobmV3X2RhdGEgPSB0ZXN0X2RhdGEpDQpgYGANCg0KYGBge3Igdml6LWZpbmFsLWZvcmVjYXN0fQ0KdGhpc19pZCA8LSAiQ2x1c3RlciAxX19CcmFuZCBHcm91cCAzMSINCmZjX2Z1dHVyZSAlPiUgDQogIGZpbHRlcihpZCA9PSB0aGlzX2lkKSAlPiUgDQogIGF1dG9wbG90KGJpbmRfcm93cyh0cmFpbl9kYXRhLCB2YWxpZF9kYXRhKSwgbGV2ZWwgPSA5MCkgKw0KICBmYWNldF93cmFwKH5pZCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKA0KICAgIGxhYmVscyA9IHNjYWxlczo6bGFiZWxfbnVtYmVyKHN1ZmZpeCA9ICJLIiwgc2NhbGUgPSAxZS0zKQ0KICApDQpgYGANCg0KIyBSZXNvdXJjZSBBbGxvY2F0aW9uDQoNClJlc291cmNlIGFsbG9jYXRpb24gbWV0aG9kcyBjYW4gYmUgdXNlZCBpbiBhbG1vc3QgZXZlcnkgYXNwZWN0IG9mIHN1cHBseSBjaGFpbiBtYW5hZ2VtZW50Lg0KVGhlIGNvbW1vbiBnb2FsIGlzIGFsbG9jYXRlIHRoZSByZXNvdXJjZXMgZWZmZWN0aXZlbHkgKGkuZS4sIG1heGltaXppbmcgdGhlIHJldmVudWUsDQptaW5pbWl6aW5nIHRoZSBjb3N0LCBvciBvcHRpbWl6aW5nIHRoZSB1dGlsaXphdGlvbiBzZXF1ZW5jZSkgd2hpbGUgc2F0aXNmeWluZyBjZXJ0YWluDQpjb25zdHJhaW50cyAoZS5nLiwgcmVzb3VyY2UgYXZhaWxhYmlsaXR5LCBjdXN0b21lciBzZXJ2aWNlIGxldmVsLCBiYWNrIG9yZGVyIGxldmVsLA0KZGVsaXZlcnkgd2luZG93KS4gSW4gdGhpcyBjYXNlLCByZXNvdXJjZXMgcmVmZXJzIHRvIGludmVzdG1lbnRzLg0KDQpJbiB0aGlzIHNlY29uZCBwYXJ0IG9mIHRoZSBjaGFsbGVuZ2UsIE5vdmFydGlzIGFzayB0byB0aGUgcGFydGljaXBhbnRzIHRvIHByb3ZpZGUgdGhlDQpvcHRpbWFsIGFsbG9jYXRpb24gb2YgdGhlaXIgcmVzb3VyY2VzIGZvciB0aGUgeWVhciAyMDE4LCBhcyB3ZWxsIGFzLCBzaG93aW5nIGRpZmZlcmVuY2VzDQpvZiBhbGxvY2F0aW9ucyBieSBjbHVzdGVyL2JyYW5kL2ludmVzdG1lbnQgdHlwZSBhbmQgaW1wYWN0IG9uIHNhbGVzLiBJbnZlc3RtZW50cyBhcmUgbm93DQpjb21wcmVzc2VkIGludG8gKkludmVzdG1lbnQgMSosICpJbnZlc3RtZW50IDIqIGFuZCAqT3RoZXJzKi4NCg0KVGhlIHRlbXBsYXRlIGZpbGUgcHJvdmlkZWQgYnkgdGhlIG9yZ2FuaXphdGlvbiBsb29rcyBhcyBmb2xsb3c6DQoNCmBgYHtyIHJlYWQtdGVtcGxhdGUtZGF0YX0NCnN1Yl90ZW1wbGF0ZV8yIDwtIA0KICByZWFkX2NzdihmaWxlLnBhdGgocGF0aF9kYXRhLCAiU3VibWlzc2lvbl9UZW1wbGF0ZTIuY3N2IikpICU+JQ0KICByZW5hbWUoDQogICAgY2x1c3RlciA9IENsdXN0ZXIsDQogICAgYnJhbmQgPSBgQnJhbmQgR3JvdXBgLCANCiAgICBgZnVuY3Rpb25gID0gRnVuY3Rpb24NCiAgKQ0KDQojIFNob3cgZmlyc3Qgcm93cyBhbmQgY29sdW1ucw0KaGVhZChzdWJfdGVtcGxhdGVfMiwgMykgJT4lIA0KICB0b19odG1sKGRpZ2l0cyA9IDEpDQpgYGANCg0KQ3JlYXRlIHRoZSBuZXcgdmFyaWFibGUgYGludmVzdG1lbnRfb3RoZXJzYCwgYW5kIHJlbW92ZSB1bm5lY2Vzc2FyeSBjb2x1bW5zLg0KDQpgYGB7ciBjcmVhdGUtaW52ZXN0bWVudC1vdGhlcnMsIGVjaG89VFJVRX0NCmFsbG9jX2RmIDwtIGFsbF9sZXZlbHNfdGJsICU+JSANCiAgbXV0YXRlKA0KICAgIGludmVzdG1lbnRfb3RoZXJzID0gaW52ZXN0bWVudF8zICsgaW52ZXN0bWVudF80ICsgDQogICAgICBpbnZlc3RtZW50XzUgKyBpbnZlc3RtZW50XzYNCiAgKSAlPiUgDQogIHNlbGVjdCgtYyhpbnZlc3RtZW50XzMsIGludmVzdG1lbnRfNCwgaW52ZXN0bWVudF81LCBpbnZlc3RtZW50XzYpKQ0KYGBgDQoNCmBgYHtyIHByaW50LWFsbG9jX2RmfQ0KIyBTaG93IGZpcnN0IHJvd3MgYW5kIGNvbHVtbnMNCmhlYWQoYWxsb2NfZGYsIDMpICU+JSANCiAgdG9faHRtbChkaWdpdHMgPSAxKQ0KYGBgDQoNCiMjIEZvcmVjYXN0aW5nIG1vZGVsDQoNCldlIG5lZWQgYSBmb3JlY2FzdGluZyBtb2RlbCB3aXRoIGludmVzdG1lbnRzIGFzIHByZWRpY3RvcnMuIElkZWFsbHksIHRoZSBtb2RlbCBvYnRhaW5lZCBpbg0KdGhlIGZpcnN0IGNoYWxsZW5nZSBpcyB0aGUgb25lIHRoYXQgd2Ugc2hvdWxkIHVzZSB0byBwZXJmb3JtIHRoZSBvcHRpbWl6YXRpb24uIEhvd2V2ZXIsDQpzb21lIG9mIHRoZSBtb2RlbHMgYXJlIHVuaXZhcmlhdGUgYW5kIGluIGFueSBjYXNlIHRoZSB2YXJpYWJsZSBgaW52ZXN0bWVudF9vdGhlcnNgIGhhcw0KYmVlbiB1c2VkLiBUaGVyZWZvcmUsIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgd2lsbCBiZSB1c2VkLCB3aGljaCB3aWxsIHVzZSB0aGUgdHJlbmQsDQp0aGUgc2Vhc29uYWxpdHkgYW5kIHRoZSBpbnZlc3RtZW50cyBhcyBwcmVkaWN0b3JzLg0KDQoqTm90ZTogd2Ugc2hvdWxkIGxvb2sgZm9yIHRoZSBiZXN0IG1vZGVsIHBvc3NpYmxlIHRoYXQgY29udGFpbnMgaW52ZXN0bWVudHMgYXMNCnByZWRpY3RvcnMuKg0KDQpgYGB7ciByZXMtYWxsb2Mtc3BsaXQtZGF0YX0NCnRyYWluX2RhdGFfYWxsb2MgPC0gYWxsb2NfZGYgJT4lIA0KICBmaWx0ZXIoZGF0ZSA8IHllYXJtb250aChmaXJzdF9kYXRlX3N1Ym1pc3Npb24pKQ0KDQp0ZXN0X2RhdGFfYWxsb2MgPC0gYWxsb2NfZGYgJT4lIA0KICBmaWx0ZXIoZGF0ZSA+PSB5ZWFybW9udGgoZmlyc3RfZGF0ZV9zdWJtaXNzaW9uKSkNCmBgYA0KDQpgYGB7ciByZXMtYWxsb2MtbW9kZWwtZGVmaW5pdGlvbiwgZWNobz1UUlVFfQ0KbW9kZWxzX2FsbG9jIDwtIGxpc3QoDQogIGxtID0gVFNMTShzYWxlc18yIH4gdHJlbmQoKSArIHNlYXNvbigpICsgaW52ZXN0bWVudF8xICsgaW52ZXN0bWVudF8yICsNCiAgICAgICAgICAgICAgaW52ZXN0bWVudF9vdGhlcnMpDQopDQpgYGANCg0KVGhlbiwgdHJhaW4gdGhlIG1vZGVscyBhbmQgZ2VuZXJhdGUgdGhlIGZvcmVjYXN0cyBmb3IgdGhlIG5leHQgMTIgbW9udGhzDQoNCmBgYHtyIHJlcy1hbGxvYy1maXQtbW9kZWxzLCBlY2hvPVRSVUV9DQojIFRyYWluIG1vZGVscw0KZml0X2FsbG9jIDwtIHRyYWluX2RhdGFfYWxsb2MgJT4lIA0KICBtb2RlbCghISFtb2RlbHNfYWxsb2MpDQoNCiMgR2VuZXJhdGUgZm9yZWNhc3RzIGZvciB0aGUgdGVzdCBwZXJpb2QNCmZjX2FsbG9jIDwtIGZpdF9hbGxvYyAlPiUgDQogIGZvcmVjYXN0KG5ld19kYXRhID0gdGVzdF9kYXRhX2FsbG9jKQ0KYGBgDQoNCmBgYHtyIHJlcy1hbGxvYy12aXotMX0NCiMgT25jZSB0aGUgbW9kZWxzIGFyZSB0cmFpbmVkLCB3ZSBjYW4gdmlzdWFsaXplIHRoZSBwcmVkaWN0aW9ucyBmb3IgdGhlIG5leHQgMTIgbW9udGhzLg0KIyBmY19hbGxvYyAlPiUgDQojICAgZmlsdGVyKGlkID09ICJDbHVzdGVyIDVfX0JyYW5kIEdyb3VwIDk2LCA5NyIpICU+JSANCiMgICBhdXRvcGxvdCh0cmFpbl9kYXRhX2FsbG9jLCBhbHBoYSA9IDAuNSwgbGV2ZWwgPSBOVUxMLCBzaXplID0gMSkgKw0KIyAgIGZhY2V0X3dyYXAofiBpZCkgKw0KIyAgIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMKSArDQojICAgc2NhbGVfeV9jb250aW51b3VzKA0KIyAgICAgbGFiZWxzID0gc2NhbGVzOjpsYWJlbF9udW1iZXIoc3VmZml4ID0gIksiLCBzY2FsZSA9IDFlLTMpDQojICAgKQ0KYGBgDQoNCmBgYHtyIGFnZ3JlZ2F0ZS1mb3JlY2FzdC0yMDE4fQ0KZmNfYWdnXzIwMTggPC0gZmNfYWxsb2MgJT4lIA0KICBhc190aWJibGUoKSAlPiUgDQogIGdyb3VwX2J5KGlkLCAubW9kZWwpICU+JSANCiAgc3VtbWFyaXNlKEZvcmVjYXN0ZWRTYWxlcyA9IHN1bSgubWVhbikpDQpgYGANCg0KIyMgU2ltdWxhdGlvbnMNCg0KV2hlbiBhIGNsb3NlZCBmb3JtIGlzIG5vdCBhdmFpbGFibGUgdG8gZXN0aW1hdGUgdGhlIHZhbHVlIG9mIGEgcGFyYW1ldGVyLCBlaXRoZXIgYmVjYXVzZQ0KaXQgZG9lcyBub3QgaGF2ZSBhIGNsb3NlZCBmb3JtIGZvcm11bGEgb3IgYmVjYXVzZSB0aGVyZSBhcmUgdG9vIG1hbnkgZmFjdG9yIHRoYXQgaW5mbHVlbmNlDQppbiB0aGUgZmluYWwgcmVzdWx0LCB0aGUgdXNlIG9mIHNpbXVsYXRpb24gdGVjaG5pcXVlcyBhcmUgY29tbW9ubHkgdXNlZCB0byBvYnNlcnZlIHRoZQ0KYmVoYXZpb3Igb2YgdGhlIHZhcmlhYmxlIHRvIGJlIGFuYWx5emVkLg0KDQpUaGUgbW9kZWwgd2lsbCBiZSBmZWQgd2l0aCB2YXJpYXRpb25zIG9mIHRoZSBpbnZlc3RtZW50cyB0byBvYnRhaW4gZGlmZmVyZW50IGZvcmVjYXN0cy4NClNvbWV0aW1lcyByZWR1Y2luZyB0aGUgYW1vdW50IGludmVzdGVkIG9uIG9uZSBzaWRlIHRvIHBsYWNlIGl0IGluIGFub3RoZXIgY2FuIGxlYWQgdG8NCmJldHRlciByZXN1bHRzLCBhbmQgdmljZSB2ZXJzYS4NCg0KVGhlIHN0ZXBzIHRvIGZvbGxvdyBhcmU6DQoNCjEuICBDcmVhdGUgYSBtYXRyaXggd2l0aCB0aGUgd2VpZ2h0cyBjb21iaW5hdGlvbnMNCjIuICBDYWxjdWxhdGUgdGhlIG5ldyBzaW11bGF0ZWQgaW52ZXN0bWVudHMNCjMuICBHZW5lcmF0ZSB0aGUgZm9yZWNhc3Qgd2l0aCB0aGUgbmV3IHNpbXVsYXRlZCB2YWx1ZXMNCg0KIyMjIFNpbXVsYXRpb24gd2VpZ2h0cyB7I3NpbXVsYXRpb24td2VpZ2h0c30NCg0KV2UgY3JlYXRlIGEgbWVzaCBvZiB2YWx1ZXMgdGhhdCBhbGxvdyB1cyB0byBldmFsdWF0ZSBkaWZmZXJlbnQgc2NlbmFyaW9zLiBUaGUgZm9sbG93aW5nDQpyZXN0cmljdGlvbnMgaGF2ZSBiZWVuIGFwcGxpZWQ6DQoNCi0gICBUaGUgd2VpZ2h0cyBtdXN0IGFkZCB1cCB0byAxDQoNCi0gICBJbiBvcmRlciB0byBkaXZlcnNpZnksIHRoZSBtYXhpbXVtIGFsbG9jYXRpb24gd2lsbCBiZSA4MCUgb2YgdGhlIGNhcGl0YWwuDQoNCmBgYHtyIHJhbmRvbS13ZWlnaHRzfQ0KIyAjIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQzMTU2NTUwL2dlbmVyYXRlLWEtcmFuZG9tLW1hdHJpeC1pbi1yLXdoaWNoLXRoZS1zdW0tb2YtZWFjaC1yb3dzLWlzLWVxdWFsLXRvLTENCg0KIyBDcmVhdGUgcmFuZG9tIHdlaWdodHMNCiMgcmFuZF93ZWlnaHRzIDwtIGZ1bmN0aW9uKG4pIHsNCiMgICB4IDwtIHNvcnQocnVuaWYobiAtIDEpKQ0KIyAgIGMoeCwgMSkgLSBjKDAsIHgpDQojIH0NCiMgd2VlaWdodHNfZGYgPC0gDQojICAgYXNfdGliYmxlKHQocmVwbGljYXRlKDQsIHJhbmRfd2VpZ2h0cygzKSkpKSAlPiUgDQojICAgc2V0X25hbWVzKGMoImludmVzdG1lbnRfMSIsICJpbnZlc3RtZW50XzIiLCAiaW52ZXN0bWVudF9vdGhlcnMiKSkNCiMgd2VlaWdodHNfZGYNCmBgYA0KDQpgYGB7ciBjcm9zc2luZy13ZWlnaHRzfQ0Kd2VpZ2h0c19kZiA8LSBjcm9zc2luZygNCiAgaW52ZXN0bWVudF8xID0gc2VxKDAsIDAuOCwgYnkgPSAwLjA1KSwgDQogIGludmVzdG1lbnRfMiA9IHNlcSgwLCAwLjgsIGJ5ID0gMC4wNSksDQogIGludmVzdG1lbnRfb3RoZXJzID0gc2VxKDAsIDAuOCwgYnkgPSAwLjA1KQ0KICApICU+JSANCiAgZmlsdGVyKGludmVzdG1lbnRfMSArIGludmVzdG1lbnRfMiArIGludmVzdG1lbnRfb3RoZXJzID09IDEpICU+JSANCiAgbXV0YXRlKA0KICAgIGNvbWJfaWQgPSByb3dfbnVtYmVyKCksIC5iZWZvcmUgPSAxTCwgDQogICAgY29tYl9kZXNjID0gc3ByaW50ZigiWyUuMmYsICUuMmYsICUuMmZdIiwgDQogICAgICAgICAgICAgICAgICAgICAgICBpbnZlc3RtZW50XzEsIGludmVzdG1lbnRfMiwgaW52ZXN0bWVudF9vdGhlcnMpDQogICkNCmBgYA0KDQpgYGB7ciBwcmludC1tZXNoLW9mLXdlaWdodHN9DQpoZWFkKHdlaWdodHNfZGYpICU+JSANCiAgdG9faHRtbChkaWdpdHMgPSAyLCBmdWxsX3dpZHRoID0gRkFMU0UpDQpgYGANCg0KQSB0b3RhbCBvZiBgciBtYXgod2VpZ2h0c19kZiRjb21iX2lkLCBuYS5ybSA9IFRSVUUpYCBjb21iaW5hdGlvbnMgcGVyIHNlcmllcyB3aWxsIGJlDQpldmFsdWF0ZWQuDQoNCiMjIyBDcmVhdGUgc2NlbmFyaW9zDQoNClRvIGdlbmVyYXRlIHRoZSBtdWx0aXBsZSBzY2VuYXJpb3Mgd2UgbXVzdCBtdWx0aXBseSB0aGUgd2VpZ2h0cyBnZW5lcmF0ZWQgaW4gdGhlIFtwcmV2aW91cw0Kc2VjdGlvbl0oI3NpbXVsYXRpb24td2VpZ2h0cykgd2l0aCB0aGUgdmFsdWVzIG9mIHRoZSBpbnZlc3RtZW50cy4gVG8gZG8gdGhpcywgd2Ugd2lsbCBoZWxwDQpvdXJzZWx2ZXMgd2l0aCB0aGUgZm9sbG93aW5nIGZ1bmN0aW9uOg0KDQpgYGB7ciBnZXQtZnV0dXJlLXNjZW5lcmFyaW9zLWZ1bmN0aW9uLCBlY2hvPVRSVUV9DQpnZXRfZnV0dXJlX3NjZW5lcmFyaW9zIDwtIGZ1bmN0aW9uKGZ1dHVyZV9kYXRhLCB3ZWlnaHRzX2RmKSB7DQogIA0KICAjIFN0b3JlIGlkIGRhdGENCiAgaWRfZGYgPC0gZnV0dXJlX2RhdGEgJT4lIA0KICAgIHNlbGVjdChpZCwgZGF0ZSkNCiAgDQogICMgQ2FsY3VsYXRlIHRvdGFsIGludmVzdGVkIGluIGVhY2ggcHJvZHVjdC1tb250aA0KICB0b3RhbF9pbnZlc3RlZCA8LSBmdXR1cmVfZGF0YSAlPiUgDQogICAgdHJhbnNtdXRlKHRvdGFsID0gcm93U3VtcyhhY3Jvc3MoY29udGFpbnMoImludmVzdG1lbnQiKSkpKQ0KICANCiAgIyBSZXBlYXQgYHRvdGFsX2ludmVzdGVkYCBjb2x1bW4gMyB0aW1lcyAob25lIGZvciBlYWNoIGludmVzdG1lbnQpDQogIGludiA8LSByZXBsaWNhdGUobiA9IDMsIHRvdGFsX2ludmVzdGVkJHRvdGFsKSANCiAgDQogICMgTG9vcCB0aHJvdWdoIHdlaWdodHMNCiAgaW52X2NvbHMgPC0gc3RyX3N1YnNldChuYW1lcyh3ZWlnaHRzX2RmKSwgImludmVzdG1lbnQiKQ0KICBzY2VuYXJpb3NfbGlzdCA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGggPSBucm93KHdlaWdodHNfZGYpKQ0KICBmb3IgKGkgaW4gc2VxKDEsIG5yb3cod2VpZ2h0c19kZikpKSB7DQogICAgDQogICAgIyBXZWlnaHRzIGZvciB0aGlzIGl0ZXJhdGlvbg0KICAgIHcgPC0gd2VpZ2h0c19kZltpLCBpbnZfY29sc10NCiAgICB3IDwtIGFzLm51bWVyaWModykNCiAgICANCiAgICAjIE5ldyBpbnZlc3RtZW50cw0KICAgIG5ld19pbnZlc3RtZW50cyA8LSB0KHcgKiB0KGludikpDQogICAgY29sbmFtZXMobmV3X2ludmVzdG1lbnRzKSA8LSBpbnZfY29scw0KDQogICAgZnV0dXJlX3NjZW5hcmlvIDwtIGlkX2RmICU+JSANCiAgICAgIGJpbmRfY29scyh3ZWlnaHRzX2RmW2ksICJjb21iX2lkIl0pICU+JSANCiAgICAgIGJpbmRfY29scyhhc190aWJibGUobmV3X2ludmVzdG1lbnRzKSkNCiAgICANCiAgICBzY2VuYXJpb3NfbGlzdFtbaV1dIDwtIGZ1dHVyZV9zY2VuYXJpbw0KICB9DQogIA0KICAjIFJldHVybiBhIGxpc3Qgd2l0aCBhbGwgdGhlIHNpbXVsYXRpb25zDQogIHNjZW5hcmlvc19saXN0DQp9DQpgYGANCg0KYGBge3IgZ2VuZXJhdGUtc2NlbmFyaW9zfQ0KIyBOZXcgcHJlZGljdG9ycyB0byBiZSBmZWQgaW50byB0aGUgbW9kZWwNCmZ1dHVyZV9zY2VuYXJpb3MgPC0gZ2V0X2Z1dHVyZV9zY2VuZXJhcmlvcyh0ZXN0X2RhdGFfYWxsb2MsIHdlaWdodHNfZGYpDQoNCiMgRm9yZWNhc3RzIHdpdGggdGhlIHNpbXVsYXRlZCBkYXRhDQpmYyA8LSBmaXRfYWxsb2MgJT4lIA0KICBmb3JlY2FzdChuZXdfZGF0YSA9IGZ1dHVyZV9zY2VuYXJpb3MpDQpgYGANCg0KSW4gZ3JheS1ibHVlIGNvbG9yIHdlIHNlZSB0aGUgcG9zc2libGUgc2NlbmFyaW9zIG9mIHRoZSBmb3JlY2FzdC4gRWFjaCBzY2VuYXJpbw0KY29ycmVzcG9uZHMgdG8gYSBkaWZmZXJlbnQgd2VpZ2h0IGNvbWJpbmF0aW9uLiBJbiBvcmFuZ2Ugd2UgaGF2ZSB0aGUgbWVkaWFuIGFuZCBpbiBhbm90aGVyDQpzaGFkZSBvZiBibHVlIHRoZSA4MHRoIHF1YW50aWxlLg0KDQpgYGB7ciB2aXotc2ltdWxhdGlvbnN9DQp0aGlzX2lkIDwtICJDbHVzdGVyIDFfX0JyYW5kIEdyb3VwIDMxIg0KDQpmY19hZ2cgPC0gZmMgJT4lIA0KICBmaWx0ZXIoaWQgPT0gdGhpc19pZCkgJT4lIA0KICBhc190aWJibGUoKSAlPiUgDQogIGdyb3VwX2J5KGlkLCAubW9kZWwsIGRhdGUpICU+JSANCiAgc3VtbWFyaXNlKA0KICAgIG1lZGlhbiA9IG1lZGlhbigubWVhbiksIA0KICAgIHVwcGVyID0gcXVhbnRpbGUoLm1lYW4sIHByb2JzID0gMC44MCksDQogICAgbG93ZXIgPSBxdWFudGlsZSgubWVhbiwgcHJvYnMgPSAwLjIwKSwgDQogICAgLmdyb3VwcyA9ICJkcm9wIg0KICApDQoNCnRyYWluX2RhdGFfYWxsb2MgJT4lIA0KICBmaWx0ZXIoaWQgPT0gdGhpc19pZCkgJT4lDQogIGF1dG9wbG90KHNhbGVzXzIpICsNCiAgYXV0b2xheWVyKA0KICAgIGZjICU+JSBmaWx0ZXIoaWQgPT0gdGhpc19pZCksDQogICAgbGV2ZWwgPSBOVUxMLCBhbHBoYSA9IDAuMDUNCiAgKSArDQogIGdlb21fbGluZSgNCiAgICBkYXRhID0gZmNfYWdnLCBtYXBwaW5nID0gYWVzKHkgPSBtZWRpYW4pLA0KICAgIGNvbG91ciA9ICJvcmFuZ2UiLCBzaXplID0gMSwgYWxwaGEgPSAwLjcNCiAgKSArDQogIHNjYWxlX3lfY29udGludW91cygNCiAgICBsYWJlbHMgPSBzY2FsZXM6OmxhYmVsX251bWJlcihzdWZmaXggPSAiSyIsIHNjYWxlID0gMWUtMykNCiAgKSArDQogIGdlb21fbGluZSgNCiAgICBkYXRhID0gZmNfYWdnLCBtYXBwaW5nID0gYWVzKHkgPSB1cHBlciksDQogICAgY29sb3VyID0gInN0ZWVsYmx1ZSIsIHNpemUgPSAxLCBhbHBoYSA9IDAuNQ0KICApICsNCiAgIyBnZW9tX2xpbmUoDQogICMgICBkYXRhID0gZmNfYWdnLCBtYXBwaW5nID0gYWVzKHkgPSBsb3dlciksIA0KICAjICAgY29sb3VyID0gIm9yYW5nZSIsIHNpemUgPSAxLCBhbHBoYSA9IDAuNQ0KICAjICkgKw0KICBmYWNldF93cmFwKH4gaWQpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMKQ0KYGBgDQoNCiMjIyBGaW5kIG9wdGltYWwgdmFsdWVzDQoNCldlIHdpbGwgdXNlIHRoZSA4MHRoIHF1YW50aWxlIGFzIHRoZSBiZXN0IGNvbWJpbmF0aW9uLiBXZSBjb3VsZCB1c2UgdGhlIGNvbWJpbmF0aW9uIG9mDQpwYXJhbWV0ZXJzIHRoYXQgaGFzIGdlbmVyYXRlZCB0aGUgZ3JlYXRlc3QgYmVuZWZpdHMsIGJ1dCBpdCB3b3VsZCBiZSBzZWxlY3QgdGhlIGJlc3QNCm9wdGltaXN0aWMgc2NlbmFyaW8gYW5kIHdlIHByZWZlciB0byBiZSBtb3JlIGNvbnNlcnZhdGl2ZS4NCg0KRmlyc3QsIHdlIGNhbGN1bGF0ZSB0aGUgdG90YWwgYW1vdW50IG9mIGVhY2ggc2ltdWxhdGlvbg0KDQpgYGB7ciBmaW5kLW9wdGltemFsLXZhbHVlcywgZWNobz1UUlVFfQ0Kc2ltdWxhdGlvbnNfYWdnIDwtIGZjICU+JSANCiAgYXNfdGliYmxlKCkgJT4lIA0KICBncm91cF9ieShpZCwgY29tYl9pZCkgJT4lIA0KICBzdW1tYXJpc2UoYWNyb3NzKGNvbnRhaW5zKCIubWVhbiIpIHwgY29udGFpbnMoImludmVzdG1lbnQiKSwgc3VtKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUgDQogIHJlbmFtZShPcHRpbWFsU2FsZXMgPSAubWVhbikgJT4lIA0KICBhcnJhbmdlKGRlc2MoT3B0aW1hbFNhbGVzKSkgJT4lIA0KICBsZWZ0X2pvaW4oc2VsZWN0KHdlaWdodHNfZGYsIGNvbWJfaWQsIGNvbWJfZGVzYyksIGJ5ID0gImNvbWJfaWQiKQ0KYGBgDQoNClRoZW4sIHNlbGVjdCB0aGUgY29tYmluYXRpb24gdGhhdCBtZWV0cyBvdXIgcmVxdWlyZW1lbnRzOiBgcGN0X3JhbmsgPD0gMC44YA0KDQpgYGB7ciBvcHRpbWFsLXZhbHVlcy1ieS1pZCwgZWNobz1UUlVFfQ0KIyBGaW5kIGJlc3QgY29tYmluYXRpb24gb2Ygd2VpZ2h0cyBmb3IgZWFjaCBgaWRgDQpvcHRpbWFsX3NpbXVsYXRpb25faWQgPC0gc2ltdWxhdGlvbnNfYWdnICU+JSANCiAgZ3JvdXBfYnkoaWQpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgY29tYl9pZCA9IGNvbWJfaWQsDQogICAgT3B0aW1hbFNhbGVzID0gT3B0aW1hbFNhbGVzLA0KICAgIHBjdF9yYW5rID0gcGVyY2VudF9yYW5rKE9wdGltYWxTYWxlcykNCiAgKSAlPiUgDQogICMgU2VsZWN0IDgwIHBlcmNlbnRpbGUNCiAgZmlsdGVyKHBjdF9yYW5rIDw9IDAuOCkgJT4lIA0KICBzbGljZV9tYXgocGN0X3JhbmspICU+JQ0KICAjIEluIGNhc2Ugb2YgdGllLCBjaG9vc2UgdGhlIGZpcnN0IG9uZS4gVGhpcyBoYXBwZW5zDQogICMgaW4gYENsdXN0ZXIgNV9fQnJhbmQgR3JvdXAgMzBgDQogIHNsaWNlX2hlYWQobiA9IDEpICU+JSANCiAgdW5ncm91cCgpDQpgYGANCg0KYGBge3Igb3B0aW1hbC12YWx1ZXMtZGF0YWZyYW1lfQ0KIyBGb3IgZWFjaCBgaWRgIHNlbGVjdCB0aGUgYmVzdCBgY29tYl9pZGAgb2YgdGhlIGFnZ3JlZ2F0ZWQgdmFsdWVzIA0Kb3B0aW1hbF9zaW11bGF0aW9uX2RmIDwtIHNpbXVsYXRpb25zX2FnZyAlPiUgDQogIHNlbWlfam9pbihvcHRpbWFsX3NpbXVsYXRpb25faWQsIGJ5ID0gYygiaWQiLCAiY29tYl9pZCIpKQ0KYGBgDQoNCkZpbmFsbHksIHByZXBhcmUgdGhlIG91dHB1dCBkYXRhDQoNCmBgYHtyIHByZXBhcmUtc3VibWlzc2lvbi1kYXRhfQ0Kc3VibWlzc2lvbl90YmwgPC0gb3B0aW1hbF9zaW11bGF0aW9uX2RmICU+JSANCiAgcGl2b3RfbG9uZ2VyKA0KICAgIGNvbHMgPSAtYyhpZCwgY29tYl9pZCwgY29tYl9kZXNjLCBPcHRpbWFsU2FsZXMpLCANCiAgICBuYW1lc190byA9ICJmdW5jdGlvbiIsIA0KICAgIHZhbHVlc190byA9ICJPcHRpbWlzZUludmVzdG1lbnQiDQogICkgJT4lIA0KICBsZWZ0X2pvaW4oZmNfYWdnXzIwMTgsIGJ5ID0gImlkIikgJT4lIA0KICBzZWxlY3QoaWQsIGBmdW5jdGlvbmAsIE9wdGltaXNlSW52ZXN0bWVudCwgRm9yZWNhc3RlZFNhbGVzLCBPcHRpbWFsU2FsZXMpDQoNCg0KaGVhZChzdWJtaXNzaW9uX3RibCkgJT4lIA0KICB0b19odG1sKGRpZ2l0cyA9IDApDQpgYGANCg0KQWNjb3JkaW5nIHRvIHRoZSBzaW11bGF0aW9uIHJlc3VsdHMsIHdlIGNvdWxkIGhhdmUgaGFkIGEgcHJvZml0IG9mIFx+IDUwMGsgaW4gMjAxOC4NCg0KYGBge3J9DQpzdWJtaXNzaW9uX3RibCAlPiUgDQogIGZpbHRlcihgZnVuY3Rpb25gID09ICJpbnZlc3RtZW50XzEiKSAlPiUgDQogIHN1bW1hcmlzZShhY3Jvc3MoaXMubnVtZXJpYywgc3VtKSkgJT4lIA0KICBtdXRhdGUoUHJvZml0ID0gT3B0aW1hbFNhbGVzIC0gRm9yZWNhc3RlZFNhbGVzKSAlPiUgDQogIGFkZF9jb2x1bW4oWWVhciA9ICIyMDE4IiwgLmJlZm9yZSA9IDFMKSAlPiUgDQogIHRvX2h0bWwoZGlnaXRzID0gMCwgZnVsbF93aWR0aCA9IEZBTFNFKQ0KYGBgDQoNCiMjIEV2b2x1dGlvbiBvZiBpbnZlc3RtZW50cw0KDQpUaGUgZXZvbHV0aW9uIG9mIHRoZSBpbnZlc3RtZW50cyBvdmVyIHRoZSB5ZWFycyBpcyBzaG93biBiZWxvdy5JdCBjYW4gYmUgc2VlbiBob3csIGluDQpnZW5lcmFsIHRlcm1zLCB0aGUgaW52ZXN0bWVudHMgZm9yIDIwMTggYXJlIGFsaWduZWQgd2l0aCB0aGUgaGlzdG9yaWNhbCB0cmVuZHMgYW5kIGRvIG5vdA0KZGlmZmVyZW50IHNoYXJwbHkgZnJvbSB0aGUgcmVjZW50IHBhc3QuIEhvd2V2ZXIsIHRoZXJlIGFyZSBzb21lIGludmVzdG1lbnRzIHRoYXQgaGF2ZSBiZWVuDQphZmZlY3RlZCBtb3JlIHRoYW4gdGhlIHJlc3QuDQoNCmBgYHtyIHdlaWdodHMtYW5hbHlzaXMtMDF9DQp3ZWlnaHRzXzIwMTggPC0gb3B0aW1hbF9zaW11bGF0aW9uX2RmICU+JSANCiAgc2VwYXJhdGUoaWQsIGludG8gPSBjKCJjbHVzdGVyIiwgImJyYW5kIiksIHNlcCA9ICJfXyIsIHJlbW92ZSA9IEZBTFNFKSAlPiUgDQogIGdyb3VwX2J5KGJyYW5kLCB5ZWFyID0gMjAxOCkgJT4lIA0KICBzdW1tYXJpc2UoYWNyb3NzKGNvbnRhaW5zKCJpbnZlc3RtZW50IiksIHN1bSkpDQoNCiMgV2VpZ2h0cyBieSB5ZWFyDQpwbG90X2RhdGEgPC0gdHJhaW5fZGF0YV9hbGxvYyAlPiUgDQogIGFzX3RpYmJsZSgpICU+JSANCiAgZ3JvdXBfYnkoYnJhbmQsIHllYXIgPSB5ZWFyKGRhdGUpKSAlPiUgDQogIHN1bW1hcmlzZShhY3Jvc3MoY29udGFpbnMoImludmVzdG1lbnQiKSwgc3VtKSkgJT4lIA0KICBmaWx0ZXIoeWVhciA+PSAyMDE0KSAlPiUgDQogIGJpbmRfcm93cyh3ZWlnaHRzXzIwMTgpDQpgYGANCg0KYGBge3Igdml6LWV2b2x1dGlvbi1pbnYtMSwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9OH0NCnBsb3RfZGF0YSAlPiUgDQogIHBpdm90X3dpZGVyKA0KICAgIGlkX2NvbHMgPSBicmFuZCwgDQogICAgbmFtZXNfZnJvbSA9IHllYXIsIA0KICAgIHZhbHVlc19mcm9tID0gaW52ZXN0bWVudF8xDQogICkgJT4lIA0KICBtdXRhdGUoaWRfbnVtID0gc3RyX3JlcGxhY2UoYnJhbmQsICJCcmFuZCBHcm91cCAiLCAiIikgJT4lIGFzLmludGVnZXIoKSkgJT4lIA0KICBwbG90X2x5KCkgJT4lIA0KICBhZGRfdHJhY2UoDQogICAgdHlwZSA9ICdwYXJjb29yZHMnLA0KICAgIGxpbmUgPSBsaXN0KA0KICAgICAgY29sb3IgPSB+aWRfbnVtLA0KICAgICAgY29sb3JzY2FsZSA9ICdKZXQnLA0KICAgICAgc2hvd3NjYWxlID0gVFJVRSwNCiAgICAgIHJldmVyc2VzY2FsZSA9IFRSVUUNCiAgICApLA0KICAgIGRpbWVuc2lvbnMgPSBsaXN0KA0KICAgICAgbGlzdChyYW5nZSA9IGMoLTI1ZTQsIDApLCBsYWJlbCA9ICIyMDE0IiwgdmFsdWVzID0gfiBgMjAxNGApLA0KICAgICAgbGlzdChyYW5nZSA9IGMoLTI1ZTQsIDApLCBsYWJlbCA9ICIyMDE1IiwgdmFsdWVzID0gfiBgMjAxNWApLA0KICAgICAgbGlzdChyYW5nZSA9IGMoLTI1ZTQsIDApLCBsYWJlbCA9ICIyMDE2IiwgdmFsdWVzID0gfiBgMjAxNmApLA0KICAgICAgbGlzdChyYW5nZSA9IGMoLTI1ZTQsIDApLCBsYWJlbCA9ICIyMDE3IiwgdmFsdWVzID0gfiBgMjAxN2ApLA0KICAgICAgbGlzdChyYW5nZSA9IGMoLTI1ZTQsIDApLCBsYWJlbCA9ICIyMDE4IiwgdmFsdWVzID0gfiBgMjAxOGApDQogICAgKQ0KICApICU+JSANCiAgbGF5b3V0KHRpdGxlID0gbGlzdCh0ZXh0ID0gIkV2b2x1dGlvbiBvZiBJbnZlc3RtZW50IDEiKSkNCmBgYA0KDQpgYGB7ciB2aXotZXZvbHV0aW9uLWludi0yLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD04fQ0KcGxvdF9kYXRhICU+JSANCiAgcGl2b3Rfd2lkZXIoDQogICAgaWRfY29scyA9IGJyYW5kLCANCiAgICBuYW1lc19mcm9tID0geWVhciwgDQogICAgdmFsdWVzX2Zyb20gPSBpbnZlc3RtZW50XzINCiAgKSAlPiUgDQogIG11dGF0ZShpZF9udW0gPSBzdHJfcmVwbGFjZShicmFuZCwgIkJyYW5kIEdyb3VwICIsICIiKSAlPiUgYXMuaW50ZWdlcigpKSAlPiUgDQogIHBsb3RfbHkoKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICB0eXBlID0gJ3BhcmNvb3JkcycsDQogICAgbGluZSA9IGxpc3QoDQogICAgICBjb2xvciA9IH5pZF9udW0sDQogICAgICBjb2xvcnNjYWxlID0gJ0pldCcsDQogICAgICBzaG93c2NhbGUgPSBUUlVFLA0KICAgICAgcmV2ZXJzZXNjYWxlID0gVFJVRQ0KICAgICksDQogICAgZGltZW5zaW9ucyA9IGxpc3QoDQogICAgICBsaXN0KHJhbmdlID0gYygtMWU1LCAwKSwgbGFiZWwgPSAiMjAxNCIsIHZhbHVlcyA9IH4gYDIwMTRgKSwNCiAgICAgIGxpc3QocmFuZ2UgPSBjKC0xZTUsIDApLCBsYWJlbCA9ICIyMDE1IiwgdmFsdWVzID0gfiBgMjAxNWApLA0KICAgICAgbGlzdChyYW5nZSA9IGMoLTFlNSwgMCksIGxhYmVsID0gIjIwMTYiLCB2YWx1ZXMgPSB+IGAyMDE2YCksDQogICAgICBsaXN0KHJhbmdlID0gYygtMWU1LCAwKSwgbGFiZWwgPSAiMjAxNyIsIHZhbHVlcyA9IH4gYDIwMTdgKSwNCiAgICAgIGxpc3QocmFuZ2UgPSBjKC0xZTUsIDApLCBsYWJlbCA9ICIyMDE4IiwgdmFsdWVzID0gfiBgMjAxOGApDQogICAgKQ0KICApICU+JSANCiAgbGF5b3V0KHRpdGxlID0gIkV2b2x1dGlvbiBvZiBJbnZlc3RtZW50IDIiKQ0KYGBgDQoNCkluIGdlbmVyYWwsIGl0IGlzIHN1Z2dlc3RlZCB0byBpbnZlc3QgbW9yZSBpbiAqb3RoZXJzKiBmb3IgdGhlIDIwMTguDQoNCmBgYHtyIHZpei1ldm9sdXRpb24taW52LW90aGVycywgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9OH0NCnBsb3RfZGF0YSAlPiUgDQogIHBpdm90X3dpZGVyKA0KICAgIGlkX2NvbHMgPSBicmFuZCwgDQogICAgbmFtZXNfZnJvbSA9IHllYXIsIA0KICAgIHZhbHVlc19mcm9tID0gaW52ZXN0bWVudF9vdGhlcnMNCiAgKSAlPiUgDQogIG11dGF0ZShpZF9udW0gPSBzdHJfcmVwbGFjZShicmFuZCwgIkJyYW5kIEdyb3VwICIsICIiKSAlPiUgYXMuaW50ZWdlcigpKSAlPiUgDQogIHBsb3RfbHkoKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICB0eXBlID0gJ3BhcmNvb3JkcycsDQogICAgbGluZSA9IGxpc3QoDQogICAgICBjb2xvciA9IH5pZF9udW0sDQogICAgICBjb2xvcnNjYWxlID0gJ0pldCcsDQogICAgICBzaG93c2NhbGUgPSBUUlVFLA0KICAgICAgcmV2ZXJzZXNjYWxlID0gVFJVRQ0KICAgICksDQogICAgZGltZW5zaW9ucyA9IGxpc3QoDQogICAgICBsaXN0KHJhbmdlID0gYygtNy4xZTQsIDApLCBsYWJlbCA9ICIyMDE0IiwgdmFsdWVzID0gfiBgMjAxNGApLA0KICAgICAgbGlzdChyYW5nZSA9IGMoLTcuMWU0LCAwKSwgbGFiZWwgPSAiMjAxNSIsIHZhbHVlcyA9IH4gYDIwMTVgKSwNCiAgICAgIGxpc3QocmFuZ2UgPSBjKC03LjFlNCwgMCksIGxhYmVsID0gIjIwMTYiLCB2YWx1ZXMgPSB+IGAyMDE2YCksDQogICAgICBsaXN0KHJhbmdlID0gYygtNy4xZTQsIDApLCBsYWJlbCA9ICIyMDE3IiwgdmFsdWVzID0gfiBgMjAxN2ApLA0KICAgICAgbGlzdChyYW5nZSA9IGMoLTcuMWU0LCAwKSwgbGFiZWwgPSAiMjAxOCIsIHZhbHVlcyA9IH4gYDIwMThgKQ0KICAgICkNCiAgKSAlPiUgDQogIGxheW91dCh0aXRsZSA9ICJFdm9sdXRpb24gb2YgSW52ZXN0bWVudCBPdGhlcnMiKQ0KYGBgDQoNCiMgV2hhdCdzIG5leHQNCg0KSSB3aWxsIGxlYXZlIHNvbWUgaWRlYXMgdGhhdCBiZSBleHBsb3JlZCB0byBleHBhbmQgdGhlIGFuYWx5c2lzDQoNCi0gICBSZW1vdmUgb3V0bGllcnMgYW5kIGNsZWFuIHRoZSBkYXRhLg0KLSAgIFRpbWUgc2VyaWVzIGNsdXN0ZXJpbmcgYmFzZWQgb24gZmVhdHVyZXMgc3VjaCBhcyB0cmVuZCBzdHJlbmd0aCwgc2Vhc29uYWxpdHksDQogICAgc3Bpa2luZXNzLCBsaW5lYXJpdHksIGF1dG9jb3JyZWxhdGlvbiwgZXRjLg0KLSAgIFRlc3QgaWYgZW5zZW1ibGVzIGRvIGltcHJvdmUgdGhlIGFjY3VyYWN5LiBUaGVyZSBhcmUgbWFueSB0eXBlcyBvZiBlbnNlbWJsZXMgdG8NCiAgICBleHBsb3JlOiBjb21iaW5hdGlvbiBlbnNlbWJsZXMsIHdlaWdodGVkIGF2ZXJhZ2UgZW5zZW1ibGVzIGJhc2VkIG9uIGNyb3NzIHZhbGlkYXRpb24NCiAgICBlcnJvcnMgb3IgZXZlbiBtb3JlIGFkdmFuY2VkIHVzaW5nIHJlZ3VsYXJpemVkIGxpbmVhciBtb2RlbHMgdG8gbmFtZSBhIGZldy4NCi0gICBVc2UgbW9yZSB0aGFuIG9uZSBmb2xkIHRvIHNlbGVjdCB0aGUgYmVzdCBtb2RlbHMsIGkuZSB1c2UgdGltZSBzZXJpZXMgY3Jvc3MNCiAgICB2YWxpZGF0aW9uLg0KLSAgIERpcmVjdGx5IGZpbmQgdGhlIGJlc3QgbW9kZWwgaW4gdGhlIGZpcnN0IGNoYWxsZW5nZSBzbyBpdCBjYW4gYmUgdXNlZCBpbiB0aGUNCiAgICBvcHRpbWl6YXRpb24gcHJvYmxlbS4NCi0gICBUcnkgb3RoZXJzIG1ldGhvZHMgc3VjaCBhcyBwcm9waGV0LCBtYWNoaW5lIGxlYXJuaW5nIG9yIG90aGVycy4NCi0gICBBY2NvdW50IGZvciB1bmNlcnRhaW50eSBtZWFzdXJlcywgYmVjYXVzZSBhbGwgaXMgbm90IGFib3V0IGFjY3VyYWN5LCBwcmVkaWN0aW9uDQogICAgaW50ZXJ2YWxzIGFyZSBhbHNvIGltcG9ydGFudC4NCi0gICBUcnkgb3RoZXIgb3B0aW1pemF0aW9uIG1ldGhvZHMNCg==